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