MojoExtension.java
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.maven.api.plugin.testing;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.io.StringReader;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Field;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.maven.api.MojoExecution;
import org.apache.maven.api.Project;
import org.apache.maven.api.Session;
import org.apache.maven.api.di.Named;
import org.apache.maven.api.di.Priority;
import org.apache.maven.api.di.Provides;
import org.apache.maven.api.di.Singleton;
import org.apache.maven.api.di.testing.MavenDIExtension;
import org.apache.maven.api.model.Build;
import org.apache.maven.api.model.ConfigurationContainer;
import org.apache.maven.api.model.Model;
import org.apache.maven.api.model.Source;
import org.apache.maven.api.plugin.Log;
import org.apache.maven.api.plugin.Mojo;
import org.apache.maven.api.plugin.descriptor.MojoDescriptor;
import org.apache.maven.api.plugin.descriptor.Parameter;
import org.apache.maven.api.plugin.descriptor.PluginDescriptor;
import org.apache.maven.api.plugin.testing.stubs.MojoExecutionStub;
import org.apache.maven.api.plugin.testing.stubs.PluginStub;
import org.apache.maven.api.plugin.testing.stubs.ProducedArtifactStub;
import org.apache.maven.api.plugin.testing.stubs.ProjectStub;
import org.apache.maven.api.plugin.testing.stubs.RepositorySystemSupplier;
import org.apache.maven.api.plugin.testing.stubs.SessionMock;
import org.apache.maven.api.services.ArtifactDeployer;
import org.apache.maven.api.services.ArtifactFactory;
import org.apache.maven.api.services.ArtifactInstaller;
import org.apache.maven.api.services.ArtifactManager;
import org.apache.maven.api.services.LocalRepositoryManager;
import org.apache.maven.api.services.ProjectBuilder;
import org.apache.maven.api.services.ProjectManager;
import org.apache.maven.api.services.RepositoryFactory;
import org.apache.maven.api.services.VersionParser;
import org.apache.maven.api.services.xml.ModelXmlFactory;
import org.apache.maven.api.xml.XmlNode;
import org.apache.maven.api.xml.XmlService;
import org.apache.maven.configuration.internal.EnhancedComponentConfigurator;
import org.apache.maven.di.Injector;
import org.apache.maven.di.Key;
import org.apache.maven.di.impl.DIException;
import org.apache.maven.impl.InternalSession;
import org.apache.maven.impl.model.DefaultModelPathTranslator;
import org.apache.maven.impl.model.DefaultPathTranslator;
import org.apache.maven.internal.impl.DefaultLog;
import org.apache.maven.internal.xml.XmlPlexusConfiguration;
import org.apache.maven.lifecycle.internal.MojoDescriptorCreator;
import org.apache.maven.model.v4.MavenMerger;
import org.apache.maven.model.v4.MavenStaxReader;
import org.apache.maven.plugin.PluginParameterExpressionEvaluatorV4;
import org.apache.maven.plugin.descriptor.io.PluginDescriptorStaxReader;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluationException;
import org.codehaus.plexus.component.configurator.expression.ExpressionEvaluator;
import org.codehaus.plexus.component.configurator.expression.TypeAwareExpressionEvaluator;
import org.codehaus.plexus.util.ReflectionUtils;
import org.codehaus.plexus.util.xml.XmlStreamReader;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
import org.eclipse.aether.RepositorySystem;
import org.junit.jupiter.api.extension.BeforeEachCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.ParameterContext;
import org.junit.jupiter.api.extension.ParameterResolutionException;
import org.junit.jupiter.api.extension.ParameterResolver;
import org.junit.platform.commons.support.AnnotationSupport;
import org.slf4j.LoggerFactory;
import static java.util.Objects.requireNonNull;
/**
* JUnit Jupiter extension that provides support for testing Maven plugins (Mojos).
* This extension handles the lifecycle of Mojo instances in tests, including instantiation,
* configuration, and dependency injection.
*
* <p>The extension is automatically registered when using the {@link MojoTest} annotation
* on a test class. It provides the following features:</p>
* <ul>
* <li>Automatic Mojo instantiation based on {@link InjectMojo} annotations</li>
* <li>Parameter injection using {@link MojoParameter} annotations</li>
* <li>POM configuration handling</li>
* <li>Project stub creation and configuration</li>
* <li>Maven session and build context setup</li>
* <li>Component dependency injection</li>
* </ul>
*
* <p>Example usage in a test class:</p>
* <pre>
* {@code
* @MojoTest
* class MyMojoTest {
* @Test
* @InjectMojo(goal = "my-goal")
* @MojoParameter(name = "outputDirectory", value = "${project.build.directory}/generated")
* void testMojoExecution(MyMojo mojo) throws Exception {
* mojo.execute();
* // verify execution results
* }
* }
* }
* </pre>
*
* <p>The extension supports two main injection scenarios:</p>
* <ol>
* <li>Method parameter injection: Mojo instances can be injected as test method parameters</li>
* <li>Field injection: Components can be injected into test class fields using {@code @Inject}</li>
* </ol>
*
* <p>For custom POM configurations, you can specify a POM file using the {@link InjectMojo#pom()}
* attribute. The extension will merge this configuration with default test project settings.</p>
*
* <p>Base directory handling:</p>
* <ul>
* <li>Plugin basedir: The directory containing the plugin project</li>
* <li>Test basedir: The directory containing test resources, configurable via {@link Basedir}</li>
* </ul>
*
* @see MojoTest
* @see InjectMojo
* @see MojoParameter
* @see Basedir
* @since 4.0.0
*/
public class MojoExtension extends MavenDIExtension implements ParameterResolver, BeforeEachCallback {
/** The base directory of the plugin being tested */
protected static String pluginBasedir;
/** The base directory for test resources */
protected static String basedir;
/**
* Gets the identifier for the current test method.
* The format is "TestClassName-testMethodName".
*
* @return the test identifier
*/
public static String getTestId() {
return context.getRequiredTestClass().getSimpleName() + "-"
+ context.getRequiredTestMethod().getName();
}
/**
* Gets the base directory for test resources.
* If not explicitly set via {@link Basedir}, returns the plugin base directory.
*
* @return the base directory path
* @throws NullPointerException if neither basedir nor plugin basedir is set
*/
public static String getBasedir() {
return requireNonNull(basedir != null ? basedir : MavenDIExtension.basedir);
}
/**
* Gets the base directory of the plugin being tested.
*
* @return the plugin base directory path
* @throws NullPointerException if plugin basedir is not set
*/
public static String getPluginBasedir() {
return requireNonNull(pluginBasedir);
}
/**
* Determines if this extension can resolve the given parameter.
* Returns true if the parameter is annotated with {@link InjectMojo} or
* if its declaring method is annotated with {@link InjectMojo}.
*
* @param parameterContext the context for the parameter being resolved
* @param extensionContext the current extension context
* @return true if this extension can resolve the parameter
*/
@Override
public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
return parameterContext.isAnnotated(InjectMojo.class)
|| parameterContext.getDeclaringExecutable().isAnnotationPresent(InjectMojo.class);
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
throws ParameterResolutionException {
try {
Class<?> holder = parameterContext.getTarget().orElseThrow().getClass();
PluginDescriptor descriptor = extensionContext
.getStore(ExtensionContext.Namespace.GLOBAL)
.get(PluginDescriptor.class, PluginDescriptor.class);
Model model =
extensionContext.getStore(ExtensionContext.Namespace.GLOBAL).get(Model.class, Model.class);
InjectMojo parameterInjectMojo =
parameterContext.getAnnotatedElement().getAnnotation(InjectMojo.class);
String goal;
if (parameterInjectMojo != null) {
String pom = parameterInjectMojo.pom();
if (pom != null && !pom.isEmpty()) {
try (Reader r = openPomUrl(holder, pom, new Path[1])) {
Model localModel = new MavenStaxReader().read(r);
model = new MavenMerger().merge(localModel, model, false, null);
model = new DefaultModelPathTranslator(new DefaultPathTranslator())
.alignToBaseDirectory(model, Paths.get(getBasedir()), null);
}
}
goal = parameterInjectMojo.goal();
} else {
InjectMojo methodInjectMojo = AnnotationSupport.findAnnotation(
parameterContext.getDeclaringExecutable(), InjectMojo.class)
.orElse(null);
if (methodInjectMojo != null) {
goal = methodInjectMojo.goal();
} else {
goal = getGoalFromMojoImplementationClass(
parameterContext.getParameter().getType());
}
}
Set<MojoParameter> mojoParameters = new LinkedHashSet<>();
for (AnnotatedElement ae :
Arrays.asList(parameterContext.getDeclaringExecutable(), parameterContext.getAnnotatedElement())) {
mojoParameters.addAll(AnnotationSupport.findRepeatableAnnotations(ae, MojoParameter.class));
}
String[] coord = mojoCoordinates(goal);
XmlNode pluginConfiguration = model.getBuild().getPlugins().stream()
.filter(p ->
Objects.equals(p.getGroupId(), coord[0]) && Objects.equals(p.getArtifactId(), coord[1]))
.findFirst()
.map(ConfigurationContainer::getConfiguration)
.orElseGet(() -> XmlNode.newInstance("config"));
List<XmlNode> children = mojoParameters.stream()
.map(mp -> XmlNode.newInstance(mp.name(), mp.value()))
.collect(Collectors.toList());
XmlNode config = XmlNode.newInstance("configuration", null, null, children, null);
pluginConfiguration = XmlService.merge(config, pluginConfiguration);
// load default config
// pluginkey = groupId : artifactId : version : goal
Mojo mojo = lookup(Mojo.class, coord[0] + ":" + coord[1] + ":" + coord[2] + ":" + coord[3]);
for (MojoDescriptor mojoDescriptor : descriptor.getMojos()) {
if (Objects.equals(mojoDescriptor.getGoal(), coord[3])) {
if (pluginConfiguration != null) {
pluginConfiguration = finalizeConfig(pluginConfiguration, mojoDescriptor);
}
}
}
Session session = getInjector().getInstance(Session.class);
Project project = getInjector().getInstance(Project.class);
MojoExecution mojoExecution = getInjector().getInstance(MojoExecution.class);
ExpressionEvaluator evaluator = new WrapEvaluator(
getInjector(), new PluginParameterExpressionEvaluatorV4(session, project, mojoExecution));
EnhancedComponentConfigurator configurator = new EnhancedComponentConfigurator();
configurator.configureComponent(
mojo, new XmlPlexusConfiguration(pluginConfiguration), evaluator, null, null);
return mojo;
} catch (Exception e) {
throw new ParameterResolutionException("Unable to resolve mojo", e);
}
}
/**
* The @Mojo annotation is only retained in the class file, not at runtime,
* so we need to actually read the class file with ASM to find the annotation and
* the goal.
*/
private static String getGoalFromMojoImplementationClass(Class<?> cl) throws IOException {
return cl.getAnnotation(Named.class).value();
}
@Override
@SuppressWarnings("checkstyle:MethodLength")
public void beforeEach(ExtensionContext context) throws Exception {
if (pluginBasedir == null) {
pluginBasedir = MavenDIExtension.getBasedir();
}
basedir = AnnotationSupport.findAnnotation(context.getElement().orElseThrow(), Basedir.class)
.map(Basedir::value)
.orElse(pluginBasedir);
if (basedir != null) {
if (basedir.isEmpty()) {
basedir = pluginBasedir + "/target/tests/"
+ context.getRequiredTestClass().getSimpleName() + "/"
+ context.getRequiredTestMethod().getName();
} else {
basedir = basedir.replace("${basedir}", pluginBasedir);
}
}
setContext(context);
/*
binder.install(ProviderMethodsModule.forObject(context.getRequiredTestInstance()));
binder.requestInjection(context.getRequiredTestInstance());
binder.bind(Log.class).toInstance(new DefaultLog(LoggerFactory.getLogger("anonymous")));
binder.bind(ExtensionContext.class).toInstance(context);
// Load maven 4 api Services interfaces and try to bind them to the (possible) mock instances
// returned by the (possibly) mock InternalSession
try {
for (ClassPath.ClassInfo clazz :
ClassPath.from(getClassLoader()).getAllClasses()) {
if ("org.apache.maven.api.services".equals(clazz.getPackageName())) {
Class<?> load = clazz.load();
if (Service.class.isAssignableFrom(load)) {
Class<Service> svc = (Class) load;
binder.bind(svc).toProvider(() -> {
try {
return getContainer()
.lookup(InternalSession.class)
.getService(svc);
} catch (ComponentLookupException e) {
throw new RuntimeException("Unable to lookup service " + svc.getName());
}
});
}
}
}
} catch (Exception e) {
throw new RuntimeException("Unable to bind session services", e);
}
*/
Path basedirPath = Paths.get(getBasedir());
InjectMojo mojo = AnnotationSupport.findAnnotation(context.getElement().get(), InjectMojo.class)
.orElse(null);
Model defaultModel = Model.newBuilder()
.groupId("myGroupId")
.artifactId("myArtifactId")
.version("1.0-SNAPSHOT")
.packaging("jar")
.build(Build.newBuilder()
.directory(basedirPath.resolve("target").toString())
.outputDirectory(basedirPath.resolve("target/classes").toString())
.sources(List.of(
Source.newBuilder()
.scope("main")
.lang("java")
.directory(basedirPath
.resolve("src/main/java")
.toString())
.build(),
Source.newBuilder()
.scope("test")
.lang("java")
.directory(basedirPath
.resolve("src/test/java")
.toString())
.build()))
.testOutputDirectory(
basedirPath.resolve("target/test-classes").toString())
.build())
.build();
Path[] modelPath = new Path[] {null};
Model tmodel = null;
if (mojo != null) {
String pom = mojo.pom();
if (pom != null && !pom.isEmpty()) {
try (Reader r = openPomUrl(context.getRequiredTestClass(), pom, modelPath)) {
tmodel = new MavenStaxReader().read(r);
}
} else {
Path pomPath = basedirPath.resolve("pom.xml");
if (Files.exists(pomPath)) {
try (Reader r = Files.newBufferedReader(pomPath)) {
tmodel = new MavenStaxReader().read(r);
modelPath[0] = pomPath;
}
}
}
}
Model model;
if (tmodel == null) {
model = defaultModel;
} else {
model = new MavenMerger().merge(tmodel, defaultModel, false, null);
}
tmodel = new DefaultModelPathTranslator(new DefaultPathTranslator())
.alignToBaseDirectory(tmodel, Paths.get(getBasedir()), null);
context.getStore(ExtensionContext.Namespace.GLOBAL).put(Model.class, tmodel);
// mojo execution
// Map<Object, Object> map = getInjector().getContext().getContextData();
PluginDescriptor pluginDescriptor;
ClassLoader classLoader = context.getRequiredTestClass().getClassLoader();
try (InputStream is = requireNonNull(
classLoader.getResourceAsStream(getPluginDescriptorLocation()),
"Unable to find plugin descriptor: " + getPluginDescriptorLocation());
Reader reader = new BufferedReader(new XmlStreamReader(is))) {
// new InterpolationFilterReader(reader, map, "${", "}");
pluginDescriptor = new PluginDescriptorStaxReader().read(reader);
}
context.getStore(ExtensionContext.Namespace.GLOBAL).put(PluginDescriptor.class, pluginDescriptor);
// for (ComponentDescriptor<?> desc : pluginDescriptor.getComponents()) {
// getContainer().addComponentDescriptor(desc);
// }
@SuppressWarnings({"unused", "MagicNumber"})
class Foo {
@Provides
@Singleton
@Priority(-10)
private InternalSession createSession() {
MojoTest mojoTest = context.getRequiredTestClass().getAnnotation(MojoTest.class);
if (mojoTest != null && mojoTest.realSession()) {
// Try to create a real session using ApiRunner without compile-time dependency
try {
Class<?> apiRunner = Class.forName("org.apache.maven.impl.standalone.ApiRunner");
Object session = apiRunner.getMethod("createSession").invoke(null);
return (InternalSession) session;
} catch (Throwable t) {
// Explicit request: do not fall back; abort the test with details instead of mocking
throw new org.opentest4j.TestAbortedException(
"@MojoTest(realSession=true) requested but could not create a real session.", t);
}
}
return SessionMock.getMockSession(getBasedir());
}
@Provides
@Singleton
@Priority(-10)
private Project createProject(InternalSession s) {
ProjectStub stub = new ProjectStub();
if (!"pom".equals(model.getPackaging())) {
ProducedArtifactStub artifact = new ProducedArtifactStub(
model.getGroupId(), model.getArtifactId(), "", model.getVersion(), model.getPackaging());
stub.setMainArtifact(artifact);
}
stub.setModel(model);
stub.setBasedir(Paths.get(MojoExtension.getBasedir()));
stub.setPomPath(modelPath[0]);
s.getService(ArtifactManager.class).setPath(stub.getPomArtifact(), modelPath[0]);
return stub;
}
@Provides
@Singleton
@Priority(-10)
private MojoExecution createMojoExecution() {
MojoExecutionStub mes = new MojoExecutionStub("executionId", null);
if (mojo != null) {
String goal = mojo.goal();
int idx = goal.lastIndexOf(':');
if (idx >= 0) {
goal = goal.substring(idx + 1);
}
mes.setGoal(goal);
for (MojoDescriptor md : pluginDescriptor.getMojos()) {
if (goal.equals(md.getGoal())) {
mes.setDescriptor(md);
}
}
requireNonNull(mes.getDescriptor());
}
PluginStub plugin = new PluginStub();
plugin.setDescriptor(pluginDescriptor);
mes.setPlugin(plugin);
return mes;
}
@Provides
@Singleton
@Priority(-10)
private Log createLog() {
return new DefaultLog(LoggerFactory.getLogger("anonymous"));
}
@Provides
static RepositorySystemSupplier newRepositorySystemSupplier() {
return new RepositorySystemSupplier();
}
@Provides
static RepositorySystem newRepositorySystem(RepositorySystemSupplier repositorySystemSupplier) {
return repositorySystemSupplier.getRepositorySystem();
}
@Provides
@Priority(10)
static RepositoryFactory newRepositoryFactory(Session session) {
return session.getService(RepositoryFactory.class);
}
@Provides
@Priority(10)
static VersionParser newVersionParser(Session session) {
return session.getService(VersionParser.class);
}
@Provides
@Priority(10)
static LocalRepositoryManager newLocalRepositoryManager(Session session) {
return session.getService(LocalRepositoryManager.class);
}
@Provides
@Priority(10)
static ArtifactInstaller newArtifactInstaller(Session session) {
return session.getService(ArtifactInstaller.class);
}
@Provides
@Priority(10)
static ArtifactDeployer newArtifactDeployer(Session session) {
return session.getService(ArtifactDeployer.class);
}
@Provides
@Priority(10)
static ArtifactManager newArtifactManager(Session session) {
return session.getService(ArtifactManager.class);
}
@Provides
@Priority(10)
static ProjectManager newProjectManager(Session session) {
return session.getService(ProjectManager.class);
}
@Provides
@Priority(10)
static ArtifactFactory newArtifactFactory(Session session) {
return session.getService(ArtifactFactory.class);
}
@Provides
@Priority(10)
static ProjectBuilder newProjectBuilder(Session session) {
return session.getService(ProjectBuilder.class);
}
@Provides
@Priority(10)
static ModelXmlFactory newModelXmlFactory(Session session) {
return session.getService(ModelXmlFactory.class);
}
}
getInjector().bindInstance(Foo.class, new Foo());
getInjector().injectInstance(context.getRequiredTestInstance());
// SessionScope sessionScope = getInjector().getInstance(SessionScope.class);
// sessionScope.enter();
// sessionScope.seed(Session.class, s);
// sessionScope.seed(InternalSession.class, s);
// MojoExecutionScope mojoExecutionScope = getInjector().getInstance(MojoExecutionScope.class);
// mojoExecutionScope.enter();
// mojoExecutionScope.seed(Project.class, p);
// mojoExecutionScope.seed(MojoExecution.class, me);
}
private Reader openPomUrl(Class<?> holder, String pom, Path[] modelPath) throws IOException {
if (pom.startsWith("file:")) {
Path path = Paths.get(getBasedir()).resolve(pom.substring("file:".length()));
modelPath[0] = path;
return Files.newBufferedReader(path);
} else if (pom.startsWith("classpath:")) {
URL url = holder.getResource(pom.substring("classpath:".length()));
if (url == null) {
throw new IllegalStateException("Unable to find pom on classpath: " + pom);
}
return new XmlStreamReader(url.openStream());
} else if (pom.contains("<project>")) {
return new StringReader(pom);
} else {
Path path = Paths.get(getBasedir()).resolve(pom);
modelPath[0] = path;
return Files.newBufferedReader(path);
}
}
protected String getPluginDescriptorLocation() {
return "META-INF/maven/plugin.xml";
}
protected String[] mojoCoordinates(String goal) throws Exception {
if (goal.matches(".*:.*:.*:.*")) {
return goal.split(":");
} else {
Path pluginPom = Paths.get(getPluginBasedir(), "pom.xml");
Xpp3Dom pluginPomDom = Xpp3DomBuilder.build(Files.newBufferedReader(pluginPom));
String artifactId = pluginPomDom.getChild("artifactId").getValue();
String groupId = resolveFromRootThenParent(pluginPomDom, "groupId");
String version = resolveFromRootThenParent(pluginPomDom, "version");
return new String[] {groupId, artifactId, version, goal};
}
}
private XmlNode finalizeConfig(XmlNode config, MojoDescriptor mojoDescriptor) {
List<XmlNode> children = new ArrayList<>();
if (mojoDescriptor != null) {
XmlNode defaultConfiguration;
defaultConfiguration = MojoDescriptorCreator.convert(mojoDescriptor);
for (Parameter parameter : mojoDescriptor.getParameters()) {
XmlNode parameterConfiguration = config.child(parameter.getName());
if (parameterConfiguration == null) {
parameterConfiguration = config.child(parameter.getAlias());
}
XmlNode parameterDefaults = defaultConfiguration.child(parameter.getName());
parameterConfiguration = XmlNode.merge(parameterConfiguration, parameterDefaults, Boolean.TRUE);
if (parameterConfiguration != null) {
Map<String, String> attributes = new HashMap<>(parameterConfiguration.attributes());
// if (isEmpty(parameterConfiguration.getAttribute("implementation"))
// && !isEmpty(parameter.getImplementation())) {
// attributes.put("implementation", parameter.getImplementation());
// }
parameterConfiguration = XmlNode.newInstance(
parameter.getName(),
parameterConfiguration.value(),
attributes,
parameterConfiguration.children(),
parameterConfiguration.inputLocation());
children.add(parameterConfiguration);
}
}
}
return XmlNode.newInstance("configuration", null, null, children, null);
}
private static Optional<Xpp3Dom> child(Xpp3Dom element, String name) {
return Optional.ofNullable(element.getChild(name));
}
private static Stream<Xpp3Dom> children(Xpp3Dom element) {
return Stream.of(element.getChildren());
}
public static XmlNode extractPluginConfiguration(String artifactId, Xpp3Dom pomDom) throws Exception {
Xpp3Dom pluginConfigurationElement = child(pomDom, "build")
.flatMap(buildElement -> child(buildElement, "plugins"))
.map(MojoExtension::children)
.orElseGet(Stream::empty)
.filter(e -> e.getChild("artifactId").getValue().equals(artifactId))
.findFirst()
.flatMap(buildElement -> child(buildElement, "configuration"))
.orElse(Xpp3DomBuilder.build(new StringReader("<configuration/>")));
return pluginConfigurationElement.getDom();
}
/**
* sometimes the parent element might contain the correct value so generalize that access
*
* TODO find out where this is probably done elsewhere
*/
private static String resolveFromRootThenParent(Xpp3Dom pluginPomDom, String element) throws Exception {
return Optional.ofNullable(child(pluginPomDom, element).orElseGet(() -> child(pluginPomDom, "parent")
.flatMap(e -> child(e, element))
.orElse(null)))
.map(Xpp3Dom::getValue)
.orElseThrow(() -> new Exception("unable to determine " + element));
}
/**
* Convenience method to obtain the value of a variable on a mojo that might not have a getter.
* <br>
* NOTE: the caller is responsible for casting to what the desired type is.
*/
public static Object getVariableValueFromObject(Object object, String variable) throws IllegalAccessException {
Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses(variable, object.getClass());
field.setAccessible(true);
return field.get(object);
}
/**
* Convenience method to obtain all variables and values from the mojo (including its superclasses)
* <br>
* Note: the values in the map are of type Object so the caller is responsible for casting to desired types.
*/
public static Map<String, Object> getVariablesAndValuesFromObject(Object object) throws IllegalAccessException {
return getVariablesAndValuesFromObject(object.getClass(), object);
}
/**
* Convenience method to obtain all variables and values from the mojo (including its superclasses)
* <br>
* Note: the values in the map are of type Object so the caller is responsible for casting to desired types.
*
* @return map of variable names and values
*/
public static Map<String, Object> getVariablesAndValuesFromObject(Class<?> clazz, Object object)
throws IllegalAccessException {
Map<String, Object> map = new HashMap<>();
Field[] fields = clazz.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
for (Field field : fields) {
map.put(field.getName(), field.get(object));
}
Class<?> superclass = clazz.getSuperclass();
if (!Object.class.equals(superclass)) {
map.putAll(getVariablesAndValuesFromObject(superclass, object));
}
return map;
}
/**
* Convenience method to set values to variables in objects that don't have setters
*/
public static void setVariableValueToObject(Object object, String variable, Object value)
throws IllegalAccessException {
Field field = ReflectionUtils.getFieldByNameIncludingSuperclasses(variable, object.getClass());
requireNonNull(field, "Field " + variable + " not found");
field.setAccessible(true);
field.set(object, value);
}
static class WrapEvaluator implements TypeAwareExpressionEvaluator {
private final Injector injector;
private final TypeAwareExpressionEvaluator evaluator;
WrapEvaluator(Injector injector, TypeAwareExpressionEvaluator evaluator) {
this.injector = injector;
this.evaluator = evaluator;
}
@Override
public Object evaluate(String expression) throws ExpressionEvaluationException {
return evaluate(expression, null);
}
@Override
public Object evaluate(String expression, Class<?> type) throws ExpressionEvaluationException {
Object value = evaluator.evaluate(expression, type);
if (value == null) {
String expr = stripTokens(expression);
if (expr != null) {
try {
value = injector.getInstance(Key.of(type, expr));
} catch (DIException e) {
// nothing
}
}
}
return value;
}
private String stripTokens(String expr) {
if (expr.startsWith("${") && expr.endsWith("}")) {
return expr.substring(2, expr.length() - 1);
}
return null;
}
@Override
public File alignToBaseDirectory(File path) {
return evaluator.alignToBaseDirectory(path);
}
}
/*
private Scope getScopeInstanceOrNull(final Injector injector, final Binding<?> binding) {
return binding.acceptScopingVisitor(new DefaultBindingScopingVisitor<Scope>() {
@Override
public Scope visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
throw new RuntimeException(String.format(
"I don't know how to handle the scopeAnnotation: %s", scopeAnnotation.getCanonicalName()));
}
@Override
public Scope visitNoScoping() {
if (binding instanceof LinkedKeyBinding) {
Binding<?> childBinding = injector.getBinding(((LinkedKeyBinding) binding).getLinkedKey());
return getScopeInstanceOrNull(injector, childBinding);
}
return null;
}
@Override
public Scope visitEagerSingleton() {
return Scopes.SINGLETON;
}
public Scope visitScope(Scope scope) {
return scope;
}
});
}*/
}