elharo commented on code in PR #31: URL: https://github.com/apache/maven-plugin-testing/pull/31#discussion_r1231341186
########## maven-plugin-testing-harness/src/main/java/org/apache/maven/plugin/testing/junit5/MojoExtension.java: ########## @@ -0,0 +1,426 @@ +/* + * 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.plugin.testing.junit5; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.google.inject.internal.ProviderMethodsModule; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.internal.MojoDescriptorCreator; +import org.apache.maven.plugin.Mojo; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.PluginParameterExpressionEvaluator; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.descriptor.Parameter; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugin.testing.ConfigurationException; +import org.apache.maven.plugin.testing.MojoLogWrapper; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.DefaultPlexusContainer; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.configurator.BasicComponentConfigurator; +import org.codehaus.plexus.component.configurator.ComponentConfigurator; +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.component.repository.ComponentDescriptor; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; +import org.codehaus.plexus.testing.PlexusExtension; +import org.codehaus.plexus.util.InterpolationFilterReader; +import org.codehaus.plexus.util.ReaderFactory; +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.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.slf4j.LoggerFactory; + +/** + * + */ +public class MojoExtension extends PlexusExtension implements ParameterResolver { + + @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 { + InjectMojo injectMojo = parameterContext + .findAnnotation(InjectMojo.class) + .orElseGet(() -> parameterContext.getDeclaringExecutable().getAnnotation(InjectMojo.class)); + List<MojoParameter> mojoParameters = parameterContext.findRepeatableAnnotations(MojoParameter.class); + Class<?> holder = parameterContext.getTarget().get().getClass(); + PluginDescriptor descriptor = extensionContext + .getStore(ExtensionContext.Namespace.GLOBAL) + .get(PluginDescriptor.class, PluginDescriptor.class); + return lookupMojo(holder, injectMojo, mojoParameters, descriptor); + } catch (Exception e) { + throw new ParameterResolutionException("Unable to resolve parameter", e); + } + } + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + Field field = PlexusExtension.class.getDeclaredField("basedir"); + field.setAccessible(true); + field.set(null, getBasedir()); + field = PlexusExtension.class.getDeclaredField("context"); + field.setAccessible(true); + field.set(this, context); + + getContainer().addComponent(getContainer(), PlexusContainer.class.getName()); + + ((DefaultPlexusContainer) getContainer()).addPlexusInjector(Collections.emptyList(), binder -> { + binder.install(ProviderMethodsModule.forObject(context.getRequiredTestInstance())); + binder.requestInjection(context.getRequiredTestInstance()); + binder.bind(Log.class).toInstance(new MojoLogWrapper(LoggerFactory.getLogger("anonymous"))); + }); + + Map<Object, Object> map = getContainer().getContext().getContextData(); + + ClassLoader classLoader = context.getRequiredTestClass().getClassLoader(); + try (InputStream is = Objects.requireNonNull( + classLoader.getResourceAsStream(getPluginDescriptorLocation()), + "Unable to find plugin descriptor: " + getPluginDescriptorLocation()); + Reader reader = new BufferedReader(new XmlStreamReader(is)); + InterpolationFilterReader interpolationReader = new InterpolationFilterReader(reader, map, "${", "}")) { + + PluginDescriptor pluginDescriptor = new PluginDescriptorBuilder().build(interpolationReader); + + // Artifact artifact = Review Comment: delete commented out code ########## maven-plugin-testing-harness/src/main/java/org/apache/maven/plugin/testing/junit5/MojoExtension.java: ########## @@ -0,0 +1,426 @@ +/* + * 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.plugin.testing.junit5; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.google.inject.internal.ProviderMethodsModule; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.internal.MojoDescriptorCreator; +import org.apache.maven.plugin.Mojo; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.PluginParameterExpressionEvaluator; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.descriptor.Parameter; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugin.testing.ConfigurationException; +import org.apache.maven.plugin.testing.MojoLogWrapper; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.DefaultPlexusContainer; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.configurator.BasicComponentConfigurator; +import org.codehaus.plexus.component.configurator.ComponentConfigurator; +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.component.repository.ComponentDescriptor; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; +import org.codehaus.plexus.testing.PlexusExtension; +import org.codehaus.plexus.util.InterpolationFilterReader; +import org.codehaus.plexus.util.ReaderFactory; +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.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.slf4j.LoggerFactory; + +/** + * + */ +public class MojoExtension extends PlexusExtension implements ParameterResolver { + + @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 { + InjectMojo injectMojo = parameterContext + .findAnnotation(InjectMojo.class) + .orElseGet(() -> parameterContext.getDeclaringExecutable().getAnnotation(InjectMojo.class)); + List<MojoParameter> mojoParameters = parameterContext.findRepeatableAnnotations(MojoParameter.class); + Class<?> holder = parameterContext.getTarget().get().getClass(); + PluginDescriptor descriptor = extensionContext + .getStore(ExtensionContext.Namespace.GLOBAL) + .get(PluginDescriptor.class, PluginDescriptor.class); + return lookupMojo(holder, injectMojo, mojoParameters, descriptor); + } catch (Exception e) { + throw new ParameterResolutionException("Unable to resolve parameter", e); + } + } + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + Field field = PlexusExtension.class.getDeclaredField("basedir"); + field.setAccessible(true); + field.set(null, getBasedir()); + field = PlexusExtension.class.getDeclaredField("context"); + field.setAccessible(true); + field.set(this, context); + + getContainer().addComponent(getContainer(), PlexusContainer.class.getName()); + + ((DefaultPlexusContainer) getContainer()).addPlexusInjector(Collections.emptyList(), binder -> { + binder.install(ProviderMethodsModule.forObject(context.getRequiredTestInstance())); + binder.requestInjection(context.getRequiredTestInstance()); + binder.bind(Log.class).toInstance(new MojoLogWrapper(LoggerFactory.getLogger("anonymous"))); + }); + + Map<Object, Object> map = getContainer().getContext().getContextData(); + + ClassLoader classLoader = context.getRequiredTestClass().getClassLoader(); + try (InputStream is = Objects.requireNonNull( + classLoader.getResourceAsStream(getPluginDescriptorLocation()), + "Unable to find plugin descriptor: " + getPluginDescriptorLocation()); + Reader reader = new BufferedReader(new XmlStreamReader(is)); + InterpolationFilterReader interpolationReader = new InterpolationFilterReader(reader, map, "${", "}")) { + + PluginDescriptor pluginDescriptor = new PluginDescriptorBuilder().build(interpolationReader); + + // Artifact artifact = + // lookup( RepositorySystem.class ).createArtifact( pluginDescriptor.getGroupId(), + // pluginDescriptor.getArtifactId(), + // pluginDescriptor.getVersion(), ".jar" ); + // + // artifact.setFile( getPluginArtifactFile() ); + // pluginDescriptor.setPluginArtifact( artifact ); + // pluginDescriptor.setArtifacts( Collections.singletonList( artifact ) ); + + context.getStore(ExtensionContext.Namespace.GLOBAL).put(PluginDescriptor.class, pluginDescriptor); + + for (ComponentDescriptor<?> desc : pluginDescriptor.getComponents()) { + getContainer().addComponentDescriptor(desc); + } + } + } + + protected String getPluginDescriptorLocation() { + return "META-INF/maven/plugin.xml"; + } + + private Mojo lookupMojo( + Class<?> holder, InjectMojo injectMojo, List<MojoParameter> mojoParameters, PluginDescriptor descriptor) + throws Exception { + String goal = injectMojo.goal(); + String pom = injectMojo.pom(); + String[] coord = mojoCoordinates(goal); + Xpp3Dom pomDom; + if (pom.startsWith("file:")) { + Path path = Paths.get(getBasedir()).resolve(pom.substring("file:".length())); + pomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(path.toFile())); + } 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); + } + pomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(url.openStream())); + } else if (pom.contains("<project>")) { + pomDom = Xpp3DomBuilder.build(new StringReader(pom)); + } else { + Path path = Paths.get(getBasedir()).resolve(pom); + pomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(path.toFile())); + } + Xpp3Dom pluginConfiguration = extractPluginConfiguration(coord[1], pomDom); + if (!mojoParameters.isEmpty()) { + List<Xpp3Dom> children = mojoParameters.stream() + .map(mp -> { + Xpp3Dom c = new Xpp3Dom(mp.name()); + c.setValue(mp.value()); + return c; + }) + .collect(Collectors.toList()); + Xpp3Dom config = new Xpp3Dom("configuration"); + children.forEach(config::addChild); + pluginConfiguration = Xpp3Dom.mergeXpp3Dom(config, pluginConfiguration); + } + Mojo mojo = lookupMojo(coord, pluginConfiguration, descriptor); + return mojo; + } + + protected String[] mojoCoordinates(String goal) throws Exception { + if (goal.matches(".*:.*:.*:.*")) { + return goal.split(":"); + } else { + Path pluginPom = Paths.get(getBasedir(), "pom.xml"); + Xpp3Dom pluginPomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(pluginPom.toFile())); + String artifactId = pluginPomDom.getChild("artifactId").getValue(); + String groupId = resolveFromRootThenParent(pluginPomDom, "groupId"); + String version = resolveFromRootThenParent(pluginPomDom, "version"); + return new String[] {groupId, artifactId, version, goal}; + } + } + + /** + * lookup the mojo while we have all of the relavent information + */ + protected Mojo lookupMojo(String[] coord, Xpp3Dom pluginConfiguration, PluginDescriptor descriptor) + throws Exception { + // 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.getImplementation(), mojo.getClass().getName())) { + if (pluginConfiguration != null) { + pluginConfiguration = finalizeConfig(pluginConfiguration, mojoDescriptor); + } + } + } + if (pluginConfiguration != null) { + MavenSession session = getContainer().lookup(MavenSession.class); + MavenProject project; + try { + project = getContainer().lookup(MavenProject.class); + } catch (ComponentLookupException e) { + project = null; + } + MojoExecution mojoExecution; + try { + mojoExecution = getContainer().lookup(MojoExecution.class); + } catch (ComponentLookupException e) { + mojoExecution = null; + } + ExpressionEvaluator evaluator = + new WrapEvaluator(getContainer(), new PluginParameterExpressionEvaluator(session, mojoExecution)); + ComponentConfigurator configurator = new BasicComponentConfigurator(); + configurator.configureComponent( + mojo, + new XmlPlexusConfiguration(pluginConfiguration), + evaluator, + getContainer().getContainerRealm()); + } + + return mojo; + } + + private Xpp3Dom finalizeConfig(Xpp3Dom config, MojoDescriptor mojoDescriptor) { + List<Xpp3Dom> children = new ArrayList<>(); + if (mojoDescriptor != null && mojoDescriptor.getParameters() != null) { + Xpp3Dom defaultConfiguration = MojoDescriptorCreator.convert(mojoDescriptor); + for (Parameter parameter : mojoDescriptor.getParameters()) { + Xpp3Dom parameterConfiguration = config.getChild(parameter.getName()); + if (parameterConfiguration == null) { + parameterConfiguration = config.getChild(parameter.getAlias()); + } + Xpp3Dom parameterDefaults = defaultConfiguration.getChild(parameter.getName()); + parameterConfiguration = Xpp3Dom.mergeXpp3Dom(parameterConfiguration, parameterDefaults, Boolean.TRUE); + if (parameterConfiguration != null) { + if (isEmpty(parameterConfiguration.getAttribute("implementation")) + && !isEmpty(parameter.getImplementation())) { + parameterConfiguration.setAttribute("implementation", parameter.getImplementation()); + } + children.add(parameterConfiguration); + } + } + } + Xpp3Dom c = new Xpp3Dom("configuration"); + children.forEach(c::addChild); + return c; + } + + private boolean isEmpty(String str) { + return str == null || str.isEmpty(); + } + + 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 Xpp3Dom 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")) + .orElseThrow( + () -> new ConfigurationException("Cannot find a configuration element for a plugin with an " + + "artifactId of " + artifactId + ".")); + return pluginConfigurationElement; + } + + /** + * 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. + * + * NOTE: the caller is responsible for casting to to what the desired type is. + * + * @param object + * @param variable + * @return object value of variable + * @throws IllegalArgumentException + */ + 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) + * + * Note: the values in the map are of type Object so the caller is responsible for casting to desired types. + * + * @param object + * @return map of variable names and values + */ + 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) + * + * Note: the values in the map are of type Object so the caller is responsible for casting to desired types. + * + * @param clazz + * @param object + * @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 + * + * @param object Review Comment: describe these or delete ########## maven-plugin-testing-harness/src/main/java/org/apache/maven/plugin/testing/junit5/MojoExtension.java: ########## @@ -0,0 +1,426 @@ +/* + * 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.plugin.testing.junit5; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.google.inject.internal.ProviderMethodsModule; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.internal.MojoDescriptorCreator; +import org.apache.maven.plugin.Mojo; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.PluginParameterExpressionEvaluator; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.descriptor.Parameter; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugin.testing.ConfigurationException; +import org.apache.maven.plugin.testing.MojoLogWrapper; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.DefaultPlexusContainer; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.configurator.BasicComponentConfigurator; +import org.codehaus.plexus.component.configurator.ComponentConfigurator; +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.component.repository.ComponentDescriptor; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; +import org.codehaus.plexus.testing.PlexusExtension; +import org.codehaus.plexus.util.InterpolationFilterReader; +import org.codehaus.plexus.util.ReaderFactory; +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.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.slf4j.LoggerFactory; + +/** Review Comment: These lines can be deleted ########## maven-plugin-testing-harness/src/main/java/org/apache/maven/plugin/testing/junit5/MojoExtension.java: ########## @@ -0,0 +1,426 @@ +/* + * 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.plugin.testing.junit5; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.google.inject.internal.ProviderMethodsModule; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.internal.MojoDescriptorCreator; +import org.apache.maven.plugin.Mojo; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.PluginParameterExpressionEvaluator; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.descriptor.Parameter; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugin.testing.ConfigurationException; +import org.apache.maven.plugin.testing.MojoLogWrapper; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.DefaultPlexusContainer; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.configurator.BasicComponentConfigurator; +import org.codehaus.plexus.component.configurator.ComponentConfigurator; +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.component.repository.ComponentDescriptor; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; +import org.codehaus.plexus.testing.PlexusExtension; +import org.codehaus.plexus.util.InterpolationFilterReader; +import org.codehaus.plexus.util.ReaderFactory; +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.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.slf4j.LoggerFactory; + +/** + * + */ +public class MojoExtension extends PlexusExtension implements ParameterResolver { + + @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 { + InjectMojo injectMojo = parameterContext + .findAnnotation(InjectMojo.class) + .orElseGet(() -> parameterContext.getDeclaringExecutable().getAnnotation(InjectMojo.class)); + List<MojoParameter> mojoParameters = parameterContext.findRepeatableAnnotations(MojoParameter.class); + Class<?> holder = parameterContext.getTarget().get().getClass(); + PluginDescriptor descriptor = extensionContext + .getStore(ExtensionContext.Namespace.GLOBAL) + .get(PluginDescriptor.class, PluginDescriptor.class); + return lookupMojo(holder, injectMojo, mojoParameters, descriptor); + } catch (Exception e) { + throw new ParameterResolutionException("Unable to resolve parameter", e); + } + } + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + Field field = PlexusExtension.class.getDeclaredField("basedir"); + field.setAccessible(true); + field.set(null, getBasedir()); + field = PlexusExtension.class.getDeclaredField("context"); + field.setAccessible(true); + field.set(this, context); + + getContainer().addComponent(getContainer(), PlexusContainer.class.getName()); + + ((DefaultPlexusContainer) getContainer()).addPlexusInjector(Collections.emptyList(), binder -> { + binder.install(ProviderMethodsModule.forObject(context.getRequiredTestInstance())); + binder.requestInjection(context.getRequiredTestInstance()); + binder.bind(Log.class).toInstance(new MojoLogWrapper(LoggerFactory.getLogger("anonymous"))); + }); + + Map<Object, Object> map = getContainer().getContext().getContextData(); + + ClassLoader classLoader = context.getRequiredTestClass().getClassLoader(); + try (InputStream is = Objects.requireNonNull( + classLoader.getResourceAsStream(getPluginDescriptorLocation()), + "Unable to find plugin descriptor: " + getPluginDescriptorLocation()); + Reader reader = new BufferedReader(new XmlStreamReader(is)); + InterpolationFilterReader interpolationReader = new InterpolationFilterReader(reader, map, "${", "}")) { + + PluginDescriptor pluginDescriptor = new PluginDescriptorBuilder().build(interpolationReader); + + // Artifact artifact = + // lookup( RepositorySystem.class ).createArtifact( pluginDescriptor.getGroupId(), + // pluginDescriptor.getArtifactId(), + // pluginDescriptor.getVersion(), ".jar" ); + // + // artifact.setFile( getPluginArtifactFile() ); + // pluginDescriptor.setPluginArtifact( artifact ); + // pluginDescriptor.setArtifacts( Collections.singletonList( artifact ) ); + + context.getStore(ExtensionContext.Namespace.GLOBAL).put(PluginDescriptor.class, pluginDescriptor); + + for (ComponentDescriptor<?> desc : pluginDescriptor.getComponents()) { + getContainer().addComponentDescriptor(desc); + } + } + } + + protected String getPluginDescriptorLocation() { + return "META-INF/maven/plugin.xml"; + } + + private Mojo lookupMojo( + Class<?> holder, InjectMojo injectMojo, List<MojoParameter> mojoParameters, PluginDescriptor descriptor) + throws Exception { + String goal = injectMojo.goal(); + String pom = injectMojo.pom(); + String[] coord = mojoCoordinates(goal); + Xpp3Dom pomDom; + if (pom.startsWith("file:")) { + Path path = Paths.get(getBasedir()).resolve(pom.substring("file:".length())); + pomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(path.toFile())); + } 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); + } + pomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(url.openStream())); + } else if (pom.contains("<project>")) { + pomDom = Xpp3DomBuilder.build(new StringReader(pom)); + } else { + Path path = Paths.get(getBasedir()).resolve(pom); + pomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(path.toFile())); + } + Xpp3Dom pluginConfiguration = extractPluginConfiguration(coord[1], pomDom); + if (!mojoParameters.isEmpty()) { + List<Xpp3Dom> children = mojoParameters.stream() + .map(mp -> { + Xpp3Dom c = new Xpp3Dom(mp.name()); + c.setValue(mp.value()); + return c; + }) + .collect(Collectors.toList()); + Xpp3Dom config = new Xpp3Dom("configuration"); + children.forEach(config::addChild); + pluginConfiguration = Xpp3Dom.mergeXpp3Dom(config, pluginConfiguration); + } + Mojo mojo = lookupMojo(coord, pluginConfiguration, descriptor); + return mojo; + } + + protected String[] mojoCoordinates(String goal) throws Exception { + if (goal.matches(".*:.*:.*:.*")) { + return goal.split(":"); + } else { + Path pluginPom = Paths.get(getBasedir(), "pom.xml"); + Xpp3Dom pluginPomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(pluginPom.toFile())); + String artifactId = pluginPomDom.getChild("artifactId").getValue(); + String groupId = resolveFromRootThenParent(pluginPomDom, "groupId"); + String version = resolveFromRootThenParent(pluginPomDom, "version"); + return new String[] {groupId, artifactId, version, goal}; + } + } + + /** + * lookup the mojo while we have all of the relavent information + */ + protected Mojo lookupMojo(String[] coord, Xpp3Dom pluginConfiguration, PluginDescriptor descriptor) + throws Exception { + // 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.getImplementation(), mojo.getClass().getName())) { + if (pluginConfiguration != null) { + pluginConfiguration = finalizeConfig(pluginConfiguration, mojoDescriptor); + } + } + } + if (pluginConfiguration != null) { + MavenSession session = getContainer().lookup(MavenSession.class); + MavenProject project; + try { + project = getContainer().lookup(MavenProject.class); + } catch (ComponentLookupException e) { + project = null; + } + MojoExecution mojoExecution; + try { + mojoExecution = getContainer().lookup(MojoExecution.class); + } catch (ComponentLookupException e) { + mojoExecution = null; + } + ExpressionEvaluator evaluator = + new WrapEvaluator(getContainer(), new PluginParameterExpressionEvaluator(session, mojoExecution)); + ComponentConfigurator configurator = new BasicComponentConfigurator(); + configurator.configureComponent( + mojo, + new XmlPlexusConfiguration(pluginConfiguration), + evaluator, + getContainer().getContainerRealm()); + } + + return mojo; + } + + private Xpp3Dom finalizeConfig(Xpp3Dom config, MojoDescriptor mojoDescriptor) { + List<Xpp3Dom> children = new ArrayList<>(); + if (mojoDescriptor != null && mojoDescriptor.getParameters() != null) { + Xpp3Dom defaultConfiguration = MojoDescriptorCreator.convert(mojoDescriptor); + for (Parameter parameter : mojoDescriptor.getParameters()) { + Xpp3Dom parameterConfiguration = config.getChild(parameter.getName()); + if (parameterConfiguration == null) { + parameterConfiguration = config.getChild(parameter.getAlias()); + } + Xpp3Dom parameterDefaults = defaultConfiguration.getChild(parameter.getName()); + parameterConfiguration = Xpp3Dom.mergeXpp3Dom(parameterConfiguration, parameterDefaults, Boolean.TRUE); + if (parameterConfiguration != null) { + if (isEmpty(parameterConfiguration.getAttribute("implementation")) + && !isEmpty(parameter.getImplementation())) { + parameterConfiguration.setAttribute("implementation", parameter.getImplementation()); + } + children.add(parameterConfiguration); + } + } + } + Xpp3Dom c = new Xpp3Dom("configuration"); + children.forEach(c::addChild); + return c; + } + + private boolean isEmpty(String str) { + return str == null || str.isEmpty(); + } + + 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 Xpp3Dom 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")) + .orElseThrow( + () -> new ConfigurationException("Cannot find a configuration element for a plugin with an " + + "artifactId of " + artifactId + ".")); + return pluginConfigurationElement; + } + + /** + * 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. + * + * NOTE: the caller is responsible for casting to to what the desired type is. Review Comment: double "to" ########## maven-plugin-testing-harness/src/main/java/org/apache/maven/plugin/testing/junit5/MojoExtension.java: ########## @@ -0,0 +1,426 @@ +/* + * 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.plugin.testing.junit5; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.Reader; +import java.io.StringReader; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.net.URL; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.google.inject.internal.ProviderMethodsModule; +import org.apache.maven.execution.MavenSession; +import org.apache.maven.lifecycle.internal.MojoDescriptorCreator; +import org.apache.maven.plugin.Mojo; +import org.apache.maven.plugin.MojoExecution; +import org.apache.maven.plugin.PluginParameterExpressionEvaluator; +import org.apache.maven.plugin.descriptor.MojoDescriptor; +import org.apache.maven.plugin.descriptor.Parameter; +import org.apache.maven.plugin.descriptor.PluginDescriptor; +import org.apache.maven.plugin.descriptor.PluginDescriptorBuilder; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugin.testing.ConfigurationException; +import org.apache.maven.plugin.testing.MojoLogWrapper; +import org.apache.maven.project.MavenProject; +import org.codehaus.plexus.DefaultPlexusContainer; +import org.codehaus.plexus.PlexusContainer; +import org.codehaus.plexus.component.configurator.BasicComponentConfigurator; +import org.codehaus.plexus.component.configurator.ComponentConfigurator; +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.component.repository.ComponentDescriptor; +import org.codehaus.plexus.component.repository.exception.ComponentLookupException; +import org.codehaus.plexus.configuration.xml.XmlPlexusConfiguration; +import org.codehaus.plexus.testing.PlexusExtension; +import org.codehaus.plexus.util.InterpolationFilterReader; +import org.codehaus.plexus.util.ReaderFactory; +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.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.slf4j.LoggerFactory; + +/** + * + */ +public class MojoExtension extends PlexusExtension implements ParameterResolver { + + @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 { + InjectMojo injectMojo = parameterContext + .findAnnotation(InjectMojo.class) + .orElseGet(() -> parameterContext.getDeclaringExecutable().getAnnotation(InjectMojo.class)); + List<MojoParameter> mojoParameters = parameterContext.findRepeatableAnnotations(MojoParameter.class); + Class<?> holder = parameterContext.getTarget().get().getClass(); + PluginDescriptor descriptor = extensionContext + .getStore(ExtensionContext.Namespace.GLOBAL) + .get(PluginDescriptor.class, PluginDescriptor.class); + return lookupMojo(holder, injectMojo, mojoParameters, descriptor); + } catch (Exception e) { + throw new ParameterResolutionException("Unable to resolve parameter", e); + } + } + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + Field field = PlexusExtension.class.getDeclaredField("basedir"); + field.setAccessible(true); + field.set(null, getBasedir()); + field = PlexusExtension.class.getDeclaredField("context"); + field.setAccessible(true); + field.set(this, context); + + getContainer().addComponent(getContainer(), PlexusContainer.class.getName()); + + ((DefaultPlexusContainer) getContainer()).addPlexusInjector(Collections.emptyList(), binder -> { + binder.install(ProviderMethodsModule.forObject(context.getRequiredTestInstance())); + binder.requestInjection(context.getRequiredTestInstance()); + binder.bind(Log.class).toInstance(new MojoLogWrapper(LoggerFactory.getLogger("anonymous"))); + }); + + Map<Object, Object> map = getContainer().getContext().getContextData(); + + ClassLoader classLoader = context.getRequiredTestClass().getClassLoader(); + try (InputStream is = Objects.requireNonNull( + classLoader.getResourceAsStream(getPluginDescriptorLocation()), + "Unable to find plugin descriptor: " + getPluginDescriptorLocation()); + Reader reader = new BufferedReader(new XmlStreamReader(is)); + InterpolationFilterReader interpolationReader = new InterpolationFilterReader(reader, map, "${", "}")) { + + PluginDescriptor pluginDescriptor = new PluginDescriptorBuilder().build(interpolationReader); + + // Artifact artifact = + // lookup( RepositorySystem.class ).createArtifact( pluginDescriptor.getGroupId(), + // pluginDescriptor.getArtifactId(), + // pluginDescriptor.getVersion(), ".jar" ); + // + // artifact.setFile( getPluginArtifactFile() ); + // pluginDescriptor.setPluginArtifact( artifact ); + // pluginDescriptor.setArtifacts( Collections.singletonList( artifact ) ); + + context.getStore(ExtensionContext.Namespace.GLOBAL).put(PluginDescriptor.class, pluginDescriptor); + + for (ComponentDescriptor<?> desc : pluginDescriptor.getComponents()) { + getContainer().addComponentDescriptor(desc); + } + } + } + + protected String getPluginDescriptorLocation() { + return "META-INF/maven/plugin.xml"; + } + + private Mojo lookupMojo( + Class<?> holder, InjectMojo injectMojo, List<MojoParameter> mojoParameters, PluginDescriptor descriptor) + throws Exception { + String goal = injectMojo.goal(); + String pom = injectMojo.pom(); + String[] coord = mojoCoordinates(goal); + Xpp3Dom pomDom; + if (pom.startsWith("file:")) { + Path path = Paths.get(getBasedir()).resolve(pom.substring("file:".length())); + pomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(path.toFile())); + } 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); + } + pomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(url.openStream())); + } else if (pom.contains("<project>")) { + pomDom = Xpp3DomBuilder.build(new StringReader(pom)); + } else { + Path path = Paths.get(getBasedir()).resolve(pom); + pomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(path.toFile())); + } + Xpp3Dom pluginConfiguration = extractPluginConfiguration(coord[1], pomDom); + if (!mojoParameters.isEmpty()) { + List<Xpp3Dom> children = mojoParameters.stream() + .map(mp -> { + Xpp3Dom c = new Xpp3Dom(mp.name()); + c.setValue(mp.value()); + return c; + }) + .collect(Collectors.toList()); + Xpp3Dom config = new Xpp3Dom("configuration"); + children.forEach(config::addChild); + pluginConfiguration = Xpp3Dom.mergeXpp3Dom(config, pluginConfiguration); + } + Mojo mojo = lookupMojo(coord, pluginConfiguration, descriptor); + return mojo; + } + + protected String[] mojoCoordinates(String goal) throws Exception { + if (goal.matches(".*:.*:.*:.*")) { + return goal.split(":"); + } else { + Path pluginPom = Paths.get(getBasedir(), "pom.xml"); + Xpp3Dom pluginPomDom = Xpp3DomBuilder.build(ReaderFactory.newXmlReader(pluginPom.toFile())); + String artifactId = pluginPomDom.getChild("artifactId").getValue(); + String groupId = resolveFromRootThenParent(pluginPomDom, "groupId"); + String version = resolveFromRootThenParent(pluginPomDom, "version"); + return new String[] {groupId, artifactId, version, goal}; + } + } + + /** + * lookup the mojo while we have all of the relavent information + */ + protected Mojo lookupMojo(String[] coord, Xpp3Dom pluginConfiguration, PluginDescriptor descriptor) + throws Exception { + // 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.getImplementation(), mojo.getClass().getName())) { + if (pluginConfiguration != null) { + pluginConfiguration = finalizeConfig(pluginConfiguration, mojoDescriptor); + } + } + } + if (pluginConfiguration != null) { + MavenSession session = getContainer().lookup(MavenSession.class); + MavenProject project; + try { + project = getContainer().lookup(MavenProject.class); + } catch (ComponentLookupException e) { + project = null; + } + MojoExecution mojoExecution; + try { + mojoExecution = getContainer().lookup(MojoExecution.class); + } catch (ComponentLookupException e) { + mojoExecution = null; + } + ExpressionEvaluator evaluator = + new WrapEvaluator(getContainer(), new PluginParameterExpressionEvaluator(session, mojoExecution)); + ComponentConfigurator configurator = new BasicComponentConfigurator(); + configurator.configureComponent( + mojo, + new XmlPlexusConfiguration(pluginConfiguration), + evaluator, + getContainer().getContainerRealm()); + } + + return mojo; + } + + private Xpp3Dom finalizeConfig(Xpp3Dom config, MojoDescriptor mojoDescriptor) { + List<Xpp3Dom> children = new ArrayList<>(); + if (mojoDescriptor != null && mojoDescriptor.getParameters() != null) { + Xpp3Dom defaultConfiguration = MojoDescriptorCreator.convert(mojoDescriptor); + for (Parameter parameter : mojoDescriptor.getParameters()) { + Xpp3Dom parameterConfiguration = config.getChild(parameter.getName()); + if (parameterConfiguration == null) { + parameterConfiguration = config.getChild(parameter.getAlias()); + } + Xpp3Dom parameterDefaults = defaultConfiguration.getChild(parameter.getName()); + parameterConfiguration = Xpp3Dom.mergeXpp3Dom(parameterConfiguration, parameterDefaults, Boolean.TRUE); + if (parameterConfiguration != null) { + if (isEmpty(parameterConfiguration.getAttribute("implementation")) + && !isEmpty(parameter.getImplementation())) { + parameterConfiguration.setAttribute("implementation", parameter.getImplementation()); + } + children.add(parameterConfiguration); + } + } + } + Xpp3Dom c = new Xpp3Dom("configuration"); + children.forEach(c::addChild); + return c; + } + + private boolean isEmpty(String str) { + return str == null || str.isEmpty(); + } + + 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 Xpp3Dom 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")) + .orElseThrow( + () -> new ConfigurationException("Cannot find a configuration element for a plugin with an " + + "artifactId of " + artifactId + ".")); + return pluginConfigurationElement; + } + + /** + * 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. + * + * NOTE: the caller is responsible for casting to to what the desired type is. + * + * @param object Review Comment: fill in or delete -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
