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