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;
032import org.twdata.maven.mojoexecutor.MojoExecutor;
033
034import javax.inject.Inject;
035import javax.inject.Singleton;
036import java.io.File;
037import java.util.Arrays;
038import java.util.Optional;
039import java.util.Properties;
040import java.util.concurrent.atomic.AtomicReference;
041
042import static io.micronaut.maven.testresources.TestResourcesConfiguration.TEST_RESOURCES_ENABLED_PROPERTY;
043import static org.twdata.maven.mojoexecutor.MojoExecutor.configuration;
044import static org.twdata.maven.mojoexecutor.MojoExecutor.executeMojo;
045import static org.twdata.maven.mojoexecutor.MojoExecutor.executionEnvironment;
046import static org.twdata.maven.mojoexecutor.MojoExecutor.goal;
047import static org.twdata.maven.mojoexecutor.MojoExecutor.plugin;
048
049/**
050 * Provides methods to execute goals on the current project.
051 *
052 * @author Álvaro Sánchez-Mariscal
053 * @since 1.1
054 */
055@Singleton
056public class ExecutorService {
057
058    private static final Logger LOG = LoggerFactory.getLogger(ExecutorService.class);
059
060    private final MojoExecutor.ExecutionEnvironment executionEnvironment;
061    private final MavenProject mavenProject;
062    private final MavenSession mavenSession;
063    private final Invoker invoker;
064
065    @SuppressWarnings("CdiInjectionPointsInspection")
066    @Inject
067    public ExecutorService(MavenProject mavenProject, MavenSession mavenSession, BuildPluginManager pluginManager,
068                           Invoker invoker) {
069        this.executionEnvironment = executionEnvironment(mavenProject, mavenSession, pluginManager);
070        this.mavenProject = mavenProject;
071        this.mavenSession = mavenSession;
072        this.invoker = invoker;
073    }
074
075    /**
076     * Executes the given goal from the given plugin coordinates.
077     *
078     * @param pluginKey The plugin coordinates in the format groupId:artifactId:version
079     * @param goal The goal to execute
080     * @throws MojoExecutionException If the goal execution fails
081     */
082    public void executeGoal(String pluginKey, String goal) throws MojoExecutionException {
083        final Plugin plugin = mavenProject.getPlugin(pluginKey);
084        if (plugin != null) {
085            var executionId = new AtomicReference<>(goal);
086            if (goal != null && goal.indexOf('#') > -1) {
087                int pos = goal.indexOf('#');
088                executionId.set(goal.substring(pos + 1));
089                goal = goal.substring(0, pos);
090            }
091            Optional<PluginExecution> execution = plugin
092                .getExecutions()
093                .stream()
094                .filter(e -> e.getId().equals(executionId.get()))
095                .findFirst();
096            Xpp3Dom configuration;
097            if (execution.isPresent()) {
098                configuration = (Xpp3Dom) execution.get().getConfiguration();
099            } else if (plugin.getConfiguration() != null) {
100                configuration = (Xpp3Dom) plugin.getConfiguration();
101            } else {
102                configuration = configuration();
103            }
104            executeMojo(plugin, goal(goal), configuration, executionEnvironment);
105        } else {
106            throw new MojoExecutionException("Plugin not found: " + pluginKey);
107        }
108    }
109
110    /**
111     * Executes a goal using the given arguments.
112     *
113     * @param pluginGroup plugin group id
114     * @param pluginArtifact plugin artifact id
115     * @param pluginVersion plugin version
116     * @param goal goal to execute
117     * @param configuration configuration for the goal
118     * @throws MojoExecutionException if the goal execution fails
119     */
120    public void executeGoal(String pluginGroup, String pluginArtifact, String pluginVersion, String goal, Xpp3Dom configuration) throws MojoExecutionException {
121        final Plugin plugin = plugin(pluginGroup, pluginArtifact, pluginVersion);
122        executeMojo(plugin, goal(goal), configuration, executionEnvironment);
123    }
124
125    /**
126     * Executes a goal using the Maven shared invoker.
127     *
128     * @param pluginKey The plugin coordinates in the format groupId:artifactId
129     * @param goal The goal to execute
130     * @return The result of the invocation
131     * @throws MavenInvocationException If the goal execution fails
132     */
133    public InvocationResult invokeGoal(String pluginKey, String goal) throws MavenInvocationException {
134        return invokeGoals(pluginKey + ":" + goal);
135    }
136
137    /**
138     * Executes a goal using the Maven shared invoker.
139     *
140     * @param goals The goals to execute
141     * @return The result of the invocation
142     * @throws MavenInvocationException If the goal execution fails
143     */
144    public InvocationResult invokeGoals(String... goals) throws MavenInvocationException {
145        var request = new DefaultInvocationRequest();
146        request.setPomFile(mavenProject.getFile());
147        File settingsFile = mavenSession.getRequest().getUserSettingsFile();
148        if (settingsFile.exists()) {
149            request.setUserSettingsFile(settingsFile);
150        }
151        var properties = new Properties();
152        properties.put(TEST_RESOURCES_ENABLED_PROPERTY, StringUtils.FALSE);
153
154        request.setLocalRepositoryDirectory(new File(mavenSession.getLocalRepository().getBasedir()));
155        request.addArgs(Arrays.asList(goals));
156        request.setBatchMode(true);
157        request.setQuiet(true);
158        request.setErrorHandler(LOG::error);
159        request.setOutputHandler(LOG::info);
160        request.setProperties(properties);
161        return invoker.execute(request);
162    }
163}