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.IOException;
26  import java.io.InputStream;
27  import java.io.Reader;
28  import java.lang.reflect.AccessibleObject;
29  import java.lang.reflect.Field;
30  import java.net.MalformedURLException;
31  import java.net.URL;
32  import java.util.ArrayList;
33  import java.util.Arrays;
34  import java.util.HashMap;
35  import java.util.List;
36  import java.util.Map;
37  import java.util.Properties;
38  
39  import org.apache.commons.io.input.XmlStreamReader;
40  import org.apache.maven.artifact.Artifact;
41  import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
42  import org.apache.maven.execution.DefaultMavenExecutionRequest;
43  import org.apache.maven.execution.DefaultMavenExecutionResult;
44  import org.apache.maven.execution.MavenExecutionRequest;
45  import org.apache.maven.execution.MavenExecutionResult;
46  import org.apache.maven.execution.MavenSession;
47  import org.apache.maven.lifecycle.internal.MojoDescriptorCreator;
48  import org.apache.maven.model.Plugin;
49  import org.apache.maven.monitor.logging.DefaultLog;
50  import org.apache.maven.plugin.Mojo;
51  import org.apache.maven.plugin.MojoExecution;
52  import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
53  import org.apache.maven.plugin.descriptor.MojoDescriptor;
54  import org.apache.maven.plugin.descriptor.Parameter;
55  import org.apache.maven.plugin.descriptor.PluginDescriptor;
56  import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder;
57  import org.apache.maven.plugin.logging.Log;
58  import org.apache.maven.project.MavenProject;
59  import org.apache.maven.repository.RepositorySystem;
60  import org.apache.maven.repository.internal.MavenRepositorySystemUtils;
61  import org.codehaus.plexus.ContainerConfiguration;
62  import org.codehaus.plexus.DefaultContainerConfiguration;
63  import org.codehaus.plexus.DefaultPlexusContainer;
64  import org.codehaus.plexus.PlexusConstants;
65  import org.codehaus.plexus.PlexusContainer;
66  import org.codehaus.plexus.PlexusContainerException;
67  import org.codehaus.plexus.PlexusTestCase;
68  import org.codehaus.plexus.classworlds.ClassWorld;
69  import org.codehaus.plexus.component.configurator.ComponentConfigurationException;
70  import org.codehaus.plexus.component.configurator.ComponentConfigurator;
71  import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
72  import org.codehaus.plexus.component.repository.ComponentDescriptor;
73  import org.codehaus.plexus.configuration.PlexusConfiguration;
74  import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration;
75  import org.codehaus.plexus.logging.LoggerManager;
76  import org.codehaus.plexus.util.IOUtil;
77  import org.codehaus.plexus.util.InterpolationFilterReader;
78  import org.codehaus.plexus.util.ReaderFactory;
79  import org.codehaus.plexus.util.ReflectionUtils;
80  import org.codehaus.plexus.util.StringUtils;
81  import org.codehaus.plexus.util.xml.Xpp3Dom;
82  import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
83  
84  import com.google.inject.Module;
85  
86  /**
87   * 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
88   * as the role hint for the mojo lookup.
89   * TODO: standardize the execution of the mojo and looking at the results, but could simply have a template method
90   * for verifying the state of the mojo post execution
91   * TODO: need a way to look at the state of the mojo without adding getters, this could be where we finally specify
92   * the expressions which extract values from the mojo.
93   * TODO: create a standard directory structure for picking up POMs to make this even easier, we really just need a testing
94   * descriptor and make this entirely declarative!
95   *
96   * @author jesse
97   * @version $Id$
98   */
99  public abstract class AbstractMojoTestCase
100     extends PlexusTestCase
101 {
102     private static final DefaultArtifactVersion MAVEN_VERSION;
103 
104     static
105     {
106         DefaultArtifactVersion version = null;
107         String path = "/META-INF/maven/org.apache.maven/maven-core/pom.properties";
108         InputStream is = AbstractMojoTestCase.class.getResourceAsStream( path );
109         try
110         {
111             Properties properties = new Properties();
112             if ( is != null )
113             {
114                 properties.load( is );
115             }
116             String property = properties.getProperty( "version" );
117             if ( property != null )
118             {
119                 version = new DefaultArtifactVersion( property );
120             }
121         }
122         catch ( IOException e )
123         {
124             // odd, where did this come from
125         }
126         finally
127         {
128             IOUtil.close( is );
129         }
130         MAVEN_VERSION = version;
131     }
132 
133     private ComponentConfigurator configurator;
134 
135     private PlexusContainer container;
136 
137     private Map<String, MojoDescriptor> mojoDescriptors;
138     
139     /*
140      * for the harness I think we have decided against going the route of using the maven project builder.
141      * instead I think we are going to try and make an instance of the localrespository and assign that
142      * to either the project stub or into the mojo directly with injection...not sure yet though.
143      */
144     //private MavenProjectBuilder projectBuilder;
145 
146     protected void setUp()
147         throws Exception
148     {
149         assertTrue( "Maven 3.2.4 or better is required",
150                     MAVEN_VERSION == null || new DefaultArtifactVersion( "3.2.3" ).compareTo( MAVEN_VERSION ) < 0 );
151 
152         configurator = getContainer().lookup( ComponentConfigurator.class, "basic" );
153 
154         InputStream is = getClass().getResourceAsStream( "/" + getPluginDescriptorLocation() );
155 
156         XmlStreamReader reader = new XmlStreamReader( is );
157 
158         InterpolationFilterReader interpolationFilterReader =
159             new InterpolationFilterReader( new BufferedReader( reader ), container.getContext().getContextData() );
160 
161         PluginDescriptor pluginDescriptor = new PluginDescriptorBuilder().build( interpolationFilterReader );
162 
163         Artifact artifact =
164             lookup( RepositorySystem.class ).createArtifact( pluginDescriptor.getGroupId(),
165                                                              pluginDescriptor.getArtifactId(),
166                                                              pluginDescriptor.getVersion(), ".jar" );
167 
168         artifact.setFile( getPluginArtifactFile() );
169         pluginDescriptor.setPluginArtifact( artifact );
170         pluginDescriptor.setArtifacts( Arrays.asList( artifact ) );
171 
172         for ( ComponentDescriptor<?> desc : pluginDescriptor.getComponents() )
173         {
174             getContainer().addComponentDescriptor( desc );
175         }
176 
177         mojoDescriptors = new HashMap<String, MojoDescriptor>();
178         for ( MojoDescriptor mojoDescriptor : pluginDescriptor.getMojos() )
179         {
180             mojoDescriptors.put( mojoDescriptor.getGoal(), mojoDescriptor );
181         }
182     }
183 
184     /**
185      * Returns best-effort plugin artifact file.
186      * <p>
187      * First, attempts to determine parent directory of META-INF directory holding the plugin descriptor. If META-INF
188      * parent directory cannot be determined, falls back to test basedir.
189      */
190     private File getPluginArtifactFile()
191         throws IOException
192     {
193         final String pluginDescriptorLocation = getPluginDescriptorLocation();
194         final URL resource = getClass().getResource( "/" + pluginDescriptorLocation );
195 
196         File file = null;
197 
198         // attempt to resolve relative to META-INF/maven/plugin.xml first
199         if ( resource != null )
200         {
201             if ( "file".equalsIgnoreCase( resource.getProtocol() ) )
202             {
203                 String path = resource.getPath();
204                 if ( path.endsWith( pluginDescriptorLocation ) )
205                 {
206                     file = new File( path.substring( 0, path.length() - pluginDescriptorLocation.length() ) );
207                 }
208             }
209             else if ( "jar".equalsIgnoreCase( resource.getProtocol() ) )
210             {
211                 // TODO is there a helper for this somewhere?
212                 try
213                 {
214                     URL jarfile = new URL( resource.getPath() );
215                     if ( "file".equalsIgnoreCase( jarfile.getProtocol() ) )
216                     {
217                         String path = jarfile.getPath();
218                         if ( path.endsWith( pluginDescriptorLocation ) )
219                         {
220                             file =
221                                 new File( path.substring( 0, path.length() - pluginDescriptorLocation.length() - 2 ) );
222                         }
223                     }
224                 }
225                 catch ( MalformedURLException e )
226                 {
227                     // not jar:file:/ URL, too bad
228                 }
229             }
230         }
231 
232         // fallback to test project basedir if couldn't resolve relative to META-INF/maven/plugin.xml
233         if ( file == null || ! file.exists() )
234         {
235             file = new File( getBasedir() );
236         }
237 
238         return file.getCanonicalFile();
239     }
240 
241     protected InputStream getPublicDescriptorStream()
242         throws Exception
243     {
244         return new FileInputStream( new File( getPluginDescriptorPath() ) );
245     }
246 
247     protected String getPluginDescriptorPath()
248     {
249         return getBasedir() + "/target/classes/META-INF/maven/plugin.xml";
250     }
251 
252     protected String getPluginDescriptorLocation()
253     {
254         return "META-INF/maven/plugin.xml";
255     }
256 
257     protected void setupContainer()
258     {
259         ContainerConfiguration cc = setupContainerConfiguration();
260         try
261         {
262             List<Module> modules = new ArrayList<Module>();
263             addGuiceModules( modules );
264             container = new DefaultPlexusContainer( cc, modules.toArray( new Module[modules.size()] ) );
265         }
266         catch ( PlexusContainerException e )
267         {
268             e.printStackTrace();
269             fail( "Failed to create plexus container." );
270         }   
271     }
272 
273     /**
274      * @since 3.0.0
275      */
276     protected void addGuiceModules( List<Module> modules )
277     {
278         // no custom guice modules by default
279     }
280 
281     protected ContainerConfiguration setupContainerConfiguration()
282     {
283         ClassWorld classWorld = new ClassWorld( "plexus.core", Thread.currentThread().getContextClassLoader() );
284 
285         ContainerConfiguration cc = new DefaultContainerConfiguration()
286           .setClassWorld( classWorld )
287           .setClassPathScanning( PlexusConstants.SCANNING_INDEX )
288           .setAutoWiring( true )
289           .setName( "maven" );      
290 
291         return cc;
292     }
293     
294     protected PlexusContainer getContainer()
295     {
296         if ( container == null )
297         {
298             setupContainer();
299         }
300 
301         return container;
302     }    
303     
304     /**
305      * Lookup the mojo leveraging the subproject pom
306      *
307      * @param goal
308      * @param pluginPom
309      * @return a Mojo instance
310      * @throws Exception
311      */
312     protected Mojo lookupMojo( String goal, String pluginPom )
313         throws Exception
314     {
315         return lookupMojo( goal, new File( pluginPom ) );
316     }
317 
318     /**
319      * Lookup an empty mojo
320      *
321      * @param goal
322      * @param pluginPom
323      * @return a Mojo instance
324      * @throws Exception
325      */
326     protected Mojo lookupEmptyMojo( String goal, String pluginPom )
327         throws Exception
328     {
329         return lookupEmptyMojo( goal, new File( pluginPom ) );
330     }
331 
332     /**
333      * Lookup the mojo leveraging the actual subprojects pom
334      *
335      * @param goal
336      * @param pom
337      * @return a Mojo instance
338      * @throws Exception
339      */
340     protected Mojo lookupMojo( String goal, File pom )
341         throws Exception
342     {
343         File pluginPom = new File( getBasedir(), "pom.xml" );
344 
345         Xpp3Dom pluginPomDom = Xpp3DomBuilder.build( ReaderFactory.newXmlReader( pluginPom ) );
346 
347         String artifactId = pluginPomDom.getChild( "artifactId" ).getValue();
348 
349         String groupId = resolveFromRootThenParent( pluginPomDom, "groupId" );
350 
351         String version = resolveFromRootThenParent( pluginPomDom, "version" );
352 
353         PlexusConfiguration pluginConfiguration = extractPluginConfiguration( artifactId, pom );
354 
355         return lookupMojo( groupId, artifactId, version, goal, pluginConfiguration );
356     }
357 
358     /**
359      * Lookup the mojo leveraging the actual subprojects pom
360      *
361      * @param goal
362      * @param pom
363      * @return a Mojo instance
364      * @throws Exception
365      */
366     protected Mojo lookupEmptyMojo( String goal, File pom )
367         throws Exception
368     {
369         File pluginPom = new File( getBasedir(), "pom.xml" );
370 
371         Xpp3Dom pluginPomDom = Xpp3DomBuilder.build( ReaderFactory.newXmlReader( pluginPom ) );
372 
373         String artifactId = pluginPomDom.getChild( "artifactId" ).getValue();
374 
375         String groupId = resolveFromRootThenParent( pluginPomDom, "groupId" );
376 
377         String version = resolveFromRootThenParent( pluginPomDom, "version" );
378 
379         return lookupMojo( groupId, artifactId, version, goal, null );
380     }
381 
382     /*
383      protected Mojo lookupMojo( String groupId, String artifactId, String version, String goal, File pom )
384      throws Exception
385      {
386      PlexusConfiguration pluginConfiguration = extractPluginConfiguration( artifactId, pom );
387 
388      return lookupMojo( groupId, artifactId, version, goal, pluginConfiguration );
389      }
390      */
391     /**
392      * lookup the mojo while we have all of the relavent information
393      *
394      * @param groupId
395      * @param artifactId
396      * @param version
397      * @param goal
398      * @param pluginConfiguration
399      * @return a Mojo instance
400      * @throws Exception
401      */
402     protected Mojo lookupMojo( String groupId, String artifactId, String version, String goal,
403                                PlexusConfiguration pluginConfiguration )
404         throws Exception
405     {
406         validateContainerStatus();
407 
408         // pluginkey = groupId : artifactId : version : goal
409 
410         Mojo mojo = (Mojo) lookup( Mojo.ROLE, groupId + ":" + artifactId + ":" + version + ":" + goal );
411 
412         LoggerManager loggerManager = (LoggerManager) getContainer().lookup( LoggerManager.class );
413         
414         Log mojoLogger = new DefaultLog( loggerManager.getLoggerForComponent( Mojo.ROLE ) );
415 
416         mojo.setLog( mojoLogger );
417 
418         if ( pluginConfiguration != null )
419         {
420             /* requires v10 of plexus container for lookup on expression evaluator
421              ExpressionEvaluator evaluator = (ExpressionEvaluator) getContainer().lookup( ExpressionEvaluator.ROLE,
422                                                                                          "stub-evaluator" );
423              */
424             ExpressionEvaluator evaluator = new ResolverExpressionEvaluatorStub();
425 
426             configurator.configureComponent( mojo, pluginConfiguration, evaluator, getContainer().getContainerRealm() );
427         }
428 
429         return mojo;
430     }
431 
432     /**
433      * 
434      * @param project
435      * @param goal
436      * @return
437      * @throws Exception
438      * @since 2.0
439      */
440     protected Mojo lookupConfiguredMojo( MavenProject project, String goal )
441         throws Exception
442     {
443         return lookupConfiguredMojo( newMavenSession( project ), newMojoExecution( goal ) );
444     }
445 
446     /**
447      * 
448      * @param session
449      * @param execution
450      * @return
451      * @throws Exception
452      * @throws ComponentConfigurationException
453      * @since 2.0
454      */
455     protected Mojo lookupConfiguredMojo( MavenSession session, MojoExecution execution )
456         throws Exception, ComponentConfigurationException
457     {
458         MavenProject project = session.getCurrentProject();
459         MojoDescriptor mojoDescriptor = execution.getMojoDescriptor();
460 
461         Mojo mojo = (Mojo) lookup( mojoDescriptor.getRole(), mojoDescriptor.getRoleHint() );
462 
463         ExpressionEvaluator evaluator = new PluginParameterExpressionEvaluator( session, execution );
464 
465         Xpp3Dom configuration = null;
466         Plugin plugin = project.getPlugin( mojoDescriptor.getPluginDescriptor().getPluginLookupKey() );
467         if ( plugin != null )
468         {
469             configuration = (Xpp3Dom) plugin.getConfiguration();
470         }
471         if ( configuration == null )
472         {
473             configuration = new Xpp3Dom( "configuration" );
474         }
475         configuration = Xpp3Dom.mergeXpp3Dom( configuration, execution.getConfiguration() );
476 
477         PlexusConfiguration pluginConfiguration = new XmlPlexusConfiguration( configuration );
478 
479         if ( mojoDescriptor.getComponentConfigurator() != null )
480         {
481             configurator = getContainer().lookup( ComponentConfigurator.class, mojoDescriptor.getComponentConfigurator() );
482         }        
483         
484         configurator.configureComponent( mojo, pluginConfiguration, evaluator, getContainer().getContainerRealm() );
485 
486         return mojo;
487     }
488 
489     /**
490      * 
491      * @param project
492      * @return
493      * @since 2.0
494      */
495     protected MavenSession newMavenSession( MavenProject project )
496     {
497         MavenExecutionRequest request = new DefaultMavenExecutionRequest();
498         MavenExecutionResult result = new DefaultMavenExecutionResult();
499 
500         MavenSession session = new MavenSession( container, MavenRepositorySystemUtils.newSession(), request, result );
501         session.setCurrentProject( project );
502         session.setProjects( Arrays.asList( project ) );
503         return session;
504     }
505 
506     /**
507      * 
508      * @param goal
509      * @return
510      * @since 2.0
511      */
512     protected MojoExecution newMojoExecution( String goal )
513     {
514         MojoDescriptor mojoDescriptor = mojoDescriptors.get( goal );
515         assertNotNull(String.format("The MojoDescriptor for the goal %s cannot be null.", goal),  mojoDescriptor );
516         MojoExecution execution = new MojoExecution( mojoDescriptor );
517         finalizeMojoConfiguration( execution );
518         return execution;
519     }
520 
521     // copy&paste from org.apache.maven.lifecycle.internal.DefaultLifecycleExecutionPlanCalculator.finalizeMojoConfiguration(MojoExecution)
522     private void finalizeMojoConfiguration( MojoExecution mojoExecution )
523     {
524         MojoDescriptor mojoDescriptor = mojoExecution.getMojoDescriptor();
525 
526         Xpp3Dom executionConfiguration = mojoExecution.getConfiguration();
527         if ( executionConfiguration == null )
528         {
529             executionConfiguration = new Xpp3Dom( "configuration" );
530         }
531 
532         Xpp3Dom defaultConfiguration = MojoDescriptorCreator.convert( mojoDescriptor );;
533 
534         Xpp3Dom finalConfiguration = new Xpp3Dom( "configuration" );
535 
536         if ( mojoDescriptor.getParameters() != null )
537         {
538             for ( Parameter parameter : mojoDescriptor.getParameters() )
539             {
540                 Xpp3Dom parameterConfiguration = executionConfiguration.getChild( parameter.getName() );
541 
542                 if ( parameterConfiguration == null )
543                 {
544                     parameterConfiguration = executionConfiguration.getChild( parameter.getAlias() );
545                 }
546 
547                 Xpp3Dom parameterDefaults = defaultConfiguration.getChild( parameter.getName() );
548 
549                 parameterConfiguration = Xpp3Dom.mergeXpp3Dom( parameterConfiguration, parameterDefaults, Boolean.TRUE );
550 
551                 if ( parameterConfiguration != null )
552                 {
553                     parameterConfiguration = new Xpp3Dom( parameterConfiguration, parameter.getName() );
554 
555                     if ( StringUtils.isEmpty( parameterConfiguration.getAttribute( "implementation" ) )
556                         && StringUtils.isNotEmpty( parameter.getImplementation() ) )
557                     {
558                         parameterConfiguration.setAttribute( "implementation", parameter.getImplementation() );
559                     }
560 
561                     finalConfiguration.addChild( parameterConfiguration );
562                 }
563             }
564         }
565 
566         mojoExecution.setConfiguration( finalConfiguration );
567     }
568 
569     /**
570      * @param artifactId
571      * @param pom
572      * @return the plexus configuration
573      * @throws Exception
574      */
575     protected PlexusConfiguration extractPluginConfiguration( String artifactId, File pom )
576         throws Exception
577     {
578         Reader reader = ReaderFactory.newXmlReader( pom );
579 
580         Xpp3Dom pomDom = Xpp3DomBuilder.build( reader );
581 
582         return extractPluginConfiguration( artifactId, pomDom );
583     }
584 
585     /**
586      * @param artifactId
587      * @param pomDom
588      * @return the plexus configuration
589      * @throws Exception
590      */
591     protected PlexusConfiguration extractPluginConfiguration( String artifactId, Xpp3Dom pomDom )
592         throws Exception
593     {
594         Xpp3Dom pluginConfigurationElement = null;
595 
596         Xpp3Dom buildElement = pomDom.getChild( "build" );
597         if ( buildElement != null )
598         {
599             Xpp3Dom pluginsRootElement = buildElement.getChild( "plugins" );
600 
601             if ( pluginsRootElement != null )
602             {
603                 Xpp3Dom[] pluginElements = pluginsRootElement.getChildren();
604 
605                 for ( Xpp3Dom pluginElement : pluginElements )
606                 {
607                     String pluginElementArtifactId = pluginElement.getChild( "artifactId" ).getValue();
608 
609                     if ( pluginElementArtifactId.equals( artifactId ) )
610                     {
611                         pluginConfigurationElement = pluginElement.getChild( "configuration" );
612 
613                         break;
614                     }
615                 }
616 
617                 if ( pluginConfigurationElement == null )
618                 {
619                     throw new ConfigurationException( "Cannot find a configuration element for a plugin with an "
620                         + "artifactId of " + artifactId + "." );
621                 }
622             }
623         }
624 
625         if ( pluginConfigurationElement == null )
626         {
627             throw new ConfigurationException( "Cannot find a configuration element for a plugin with an artifactId of "
628                 + artifactId + "." );
629         }
630 
631         return new XmlPlexusConfiguration( pluginConfigurationElement );
632     }
633 
634     /**
635      * Configure the mojo
636      *
637      * @param mojo
638      * @param artifactId
639      * @param pom
640      * @return a Mojo instance
641      * @throws Exception
642      */
643     protected Mojo configureMojo( Mojo mojo, String artifactId, File pom )
644         throws Exception
645     {
646         validateContainerStatus();
647 
648         PlexusConfiguration pluginConfiguration = extractPluginConfiguration( artifactId, pom );
649 
650         ExpressionEvaluator evaluator = new ResolverExpressionEvaluatorStub();
651 
652         configurator.configureComponent( mojo, pluginConfiguration, evaluator, getContainer().getContainerRealm() );
653 
654         return mojo;
655     }
656 
657     /**
658      * Configure the mojo with the given plexus configuration
659      *
660      * @param mojo
661      * @param pluginConfiguration
662      * @return a Mojo instance
663      * @throws Exception
664      */
665     protected Mojo configureMojo( Mojo mojo, PlexusConfiguration pluginConfiguration )
666         throws Exception
667     {
668         validateContainerStatus();
669 
670         ExpressionEvaluator evaluator = new ResolverExpressionEvaluatorStub();
671 
672         configurator.configureComponent( mojo, pluginConfiguration, evaluator, getContainer().getContainerRealm() );
673 
674         return mojo;
675     }
676 
677     /**
678      * Convenience method to obtain the value of a variable on a mojo that might not have a getter.
679      *
680      * NOTE: the caller is responsible for casting to to what the desired type is.
681      *
682      * @param object
683      * @param variable
684      * @return object value of variable
685      * @throws IllegalArgumentException
686      */
687     protected Object getVariableValueFromObject( Object object, String variable )
688         throws IllegalAccessException
689     {
690         Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses( variable, object.getClass() );
691 
692         field.setAccessible( true );
693 
694         return field.get( object );
695     }
696 
697     /**
698      * Convenience method to obtain all variables and values from the mojo (including its superclasses)
699      *
700      * Note: the values in the map are of type Object so the caller is responsible for casting to desired types.
701      *
702      * @param object
703      * @return map of variable names and values
704      */
705     protected Map<String, Object> getVariablesAndValuesFromObject( Object object )
706         throws IllegalAccessException
707     {
708         return getVariablesAndValuesFromObject( object.getClass(), object );
709     }
710 
711     /**
712      * Convenience method to obtain all variables and values from the mojo (including its superclasses)
713      *
714      * Note: the values in the map are of type Object so the caller is responsible for casting to desired types.
715      *
716      * @param clazz
717      * @param object
718      * @return map of variable names and values
719      */
720     protected Map<String, Object> getVariablesAndValuesFromObject( Class<?> clazz, Object object )
721         throws IllegalAccessException
722     {
723         Map<String, Object> map = new HashMap<String, Object>();
724 
725         Field[] fields = clazz.getDeclaredFields();
726 
727         AccessibleObject.setAccessible( fields, true );
728 
729         for ( Field field : fields )
730         {
731             map.put( field.getName(), field.get( object ) );
732         }
733 
734         Class<?> superclass = clazz.getSuperclass();
735 
736         if ( !Object.class.equals( superclass ) )
737         {
738             map.putAll( getVariablesAndValuesFromObject( superclass, object ) );
739         }
740 
741         return map;
742     }
743 
744     /**
745      * Convenience method to set values to variables in objects that don't have setters
746      *
747      * @param object
748      * @param variable
749      * @param value
750      * @throws IllegalAccessException
751      */
752     protected void setVariableValueToObject( Object object, String variable, Object value )
753         throws IllegalAccessException
754     {
755         Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses( variable, object.getClass() );
756 
757         field.setAccessible( true );
758 
759         field.set( object, value );
760     }
761 
762     /**
763      * sometimes the parent element might contain the correct value so generalize that access
764      *
765      * TODO find out where this is probably done elsewhere
766      *
767      * @param pluginPomDom
768      * @param element
769      * @return
770      * @throws Exception
771      */
772     private String resolveFromRootThenParent( Xpp3Dom pluginPomDom, String element )
773         throws Exception
774     {
775         Xpp3Dom elementDom = pluginPomDom.getChild( element );
776 
777         // parent might have the group Id so resolve it
778         if ( elementDom == null )
779         {
780             Xpp3Dom pluginParentDom = pluginPomDom.getChild( "parent" );
781 
782             if ( pluginParentDom != null )
783             {
784                 elementDom = pluginParentDom.getChild( element );
785 
786                 if ( elementDom == null )
787                 {
788                     throw new Exception( "unable to determine " + element );
789                 }
790 
791                 return elementDom.getValue();
792             }
793 
794             throw new Exception( "unable to determine " + element );
795         }
796 
797         return elementDom.getValue();
798     }
799 
800     /**
801      * We should make sure this is called in each method that makes use of the container,
802      * otherwise we throw ugly NPE's
803      *
804      * crops up when the subclassing code defines the setUp method but doesn't call super.setUp()
805      *
806      * @throws Exception
807      */
808     private void validateContainerStatus()
809         throws Exception
810     {
811         if ( getContainer() != null )
812         {
813             return;
814         }
815 
816         throw new Exception( "container is null, make sure super.setUp() is called" );
817     }    
818 }