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