001/*
002 * Copyright 2017-2022 original authors
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package io.micronaut.maven.services;
017
018import io.micronaut.core.util.StringUtils;
019import org.apache.maven.execution.MavenSession;
020import org.apache.maven.model.Plugin;
021import org.apache.maven.model.PluginExecution;
022import org.apache.maven.plugin.BuildPluginManager;
023import org.apache.maven.plugin.MojoExecutionException;
024import org.apache.maven.project.MavenProject;
025import org.apache.maven.shared.invoker.DefaultInvocationRequest;
026import org.apache.maven.shared.invoker.InvocationResult;
027import org.apache.maven.shared.invoker.Invoker;
028import org.apache.maven.shared.invoker.MavenInvocationException;
029import org.codehaus.plexus.util.xml.Xpp3Dom;
030import org.slf4j.Logger;
031import org.slf4j.LoggerFactory;
032
033import javax.inject.Inject;
034import javax.inject.Singleton;
035import java.io.File;
036import java.util.Arrays;
037import java.util.Optional;
038import java.util.Properties;
039import java.util.concurrent.atomic.AtomicReference;
040
041import static io.micronaut.maven.testresources.TestResourcesConfiguration.TEST_RESOURCES_ENABLED_PROPERTY;
042import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration;
043import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo;
044import static org.twdata.maven.mojoexecutor.MojoExecutor.executionEnvironment;
045import static org.twdata.maven.mojoexecutor.MojoExecutor.goal;
046import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin;
047
048/**
049 * Provides methods to execute goals on the current project.
050 *
051 * @author Álvaro Sánchez-Mariscal
052 * @since 1.1
053 */
054@Singleton
055public class ExecutorService {
056
057    private static final Logger LOG = LoggerFactory.getLogger(ExecutorService.class);
058
059    private final BuildPluginManager pluginManager;
060    private final MavenProject mavenProject;
061    private final MavenSession mavenSession;
062    private final Invoker invoker;
063
064    @SuppressWarnings("CdiInjectionPointsInspection")
065    @Inject
066    public ExecutorService(MavenProject mavenProject, MavenSession mavenSession, BuildPluginManager pluginManager,
067                           Invoker invoker) {
068        this.pluginManager = pluginManager;
069        this.mavenProject = mavenProject;
070        this.mavenSession = mavenSession;
071        this.invoker = invoker;
072    }
073
074    /**
075     * Executes the given goal from the given plugin coordinates.
076     *
077     * @param pluginKey The plugin coordinates in the format groupId:artifactId:version
078     * @param goal The goal to execute
079     * @throws MojoExecutionException If the goal execution fails
080     */
081    public void executeGoal(String pluginKey, String goal) throws MojoExecutionException {
082        executeGoal(mavenProject, pluginKey, goal, null);
083    }
084
085    public final void executeGoal(MavenProject project, String pluginKey, String goal) throws MojoExecutionException {
086        executeGoal(project, pluginKey, goal, null);
087    }
088
089    public final void executeGoal(MavenProject project, String pluginKey, String goal, Xpp3Dom overriddenConfiguration) throws MojoExecutionException {
090        MavenProject targetProject = project == null ? mavenProject : project;
091        final Plugin plugin = targetProject.getPlugin(pluginKey);
092        if (plugin != null) {
093            String goalName = goal;
094            var executionId = new AtomicReference<>(goalName);
095            if (goalName != null && goalName.indexOf('#') > -1) {
096                int pos = goalName.indexOf('#');
097                executionId.set(goal.substring(pos + 1));
098                goalName = goalName.substring(0, pos);
099            }
100            Optional<PluginExecution> execution = plugin
101                .getExecutions()
102                .stream()
103                .filter(e -> e.getId().equals(executionId.get()))
104                .findFirst();
105            Xpp3Dom configuration;
106            if (overriddenConfiguration != null) {
107                configuration = overriddenConfiguration;
108            } else if (execution.isPresent()) {
109                configuration = (Xpp3Dom) execution.get().getConfiguration();
110            } else if (plugin.getConfiguration() != null) {
111                configuration = (Xpp3Dom) plugin.getConfiguration();
112            } else {
113                configuration = configuration();
114            }
115            executeMojo(plugin, goal(goalName), configuration, executionEnvironment(targetProject, mavenSession, pluginManager));
116        } else {
117            throw new MojoExecutionException("Plugin not found: " + pluginKey);
118        }
119    }
120
121    /**
122     * Executes a goal using the given arguments.
123     *
124     * @param pluginGroup plugin group id
125     * @param pluginArtifact plugin artifact id
126     * @param pluginVersion plugin version
127     * @param goal goal to execute
128     * @param configuration configuration for the goal
129     * @throws MojoExecutionException if the goal execution fails
130     */
131    public void executeGoal(String pluginGroup, String pluginArtifact, String pluginVersion, String goal, Xpp3Dom configuration) throws MojoExecutionException {
132        final Plugin plugin = plugin(pluginGroup, pluginArtifact, pluginVersion);
133        executeMojo(plugin, goal(goal), configuration, executionEnvironment(mavenProject, mavenSession, pluginManager));
134    }
135
136    /**
137     * Executes a goal using the Maven shared invoker.
138     *
139     * @param pluginKey The plugin coordinates in the format groupId:artifactId
140     * @param goal The goal to execute
141     * @return The result of the invocation
142     * @throws MavenInvocationException If the goal execution fails
143     */
144    public InvocationResult invokeGoal(String pluginKey, String goal) throws MavenInvocationException {
145        return invokeGoals(pluginKey + ":" + goal);
146    }
147
148    /**
149     * Executes a goal using the Maven shared invoker.
150     *
151     * @param goals The goals to execute
152     * @return The result of the invocation
153     * @throws MavenInvocationException If the goal execution fails
154     */
155    public InvocationResult invokeGoals(String... goals) throws MavenInvocationException {
156        return invokeGoals(mavenProject, goals);
157    }
158
159    /**
160     * Executes a goal using the Maven shared invoker.
161     *
162     * @param project The Maven project
163     * @param goals The goals to execute
164     * @return The result of the invocation
165     * @throws MavenInvocationException If the goal execution fails
166     */
167
168    public InvocationResult invokeGoals(MavenProject project, String... goals) throws MavenInvocationException {
169        var request = new DefaultInvocationRequest();
170        request.setPomFile(project.getFile());
171        File settingsFile = mavenSession.getRequest().getUserSettingsFile();
172        if (settingsFile.exists()) {
173            request.setUserSettingsFile(settingsFile);
174        }
175        var properties = new Properties();
176        properties.put(TEST_RESOURCES_ENABLED_PROPERTY, StringUtils.FALSE);
177
178        request.setLocalRepositoryDirectory(new File(mavenSession.getLocalRepository().getBasedir()));
179        request.addArgs(Arrays.asList(goals));
180        request.setBatchMode(true);
181        request.setQuiet(true);
182        request.setAlsoMake(true);
183        request.setErrorHandler(LOG::error);
184        request.setOutputHandler(LOG::info);
185        request.setProperties(properties);
186        return invoker.execute(request);
187    }
188}