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