blob: 9301355d92353a924c0036c12bee82421953fc23 [file] [log] [blame]
/*
* 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.lifecycle.internal;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.maven.api.services.Lookup;
import org.apache.maven.api.xml.XmlNode;
import org.apache.maven.lifecycle.DefaultLifecycles;
import org.apache.maven.lifecycle.LifeCyclePluginAnalyzer;
import org.apache.maven.lifecycle.Lifecycle;
import org.apache.maven.lifecycle.mapping.LifecycleMapping;
import org.apache.maven.lifecycle.mapping.LifecycleMojo;
import org.apache.maven.lifecycle.mapping.LifecyclePhase;
import org.apache.maven.model.InputLocation;
import org.apache.maven.model.InputSource;
import org.apache.maven.model.Plugin;
import org.apache.maven.model.PluginExecution;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.util.Objects.requireNonNull;
/**
* <strong>NOTE:</strong> This class is not part of any public api and can be changed or deleted without prior notice.
*
* @since 3.0
*/
@Singleton
@Named
public class DefaultLifecyclePluginAnalyzer implements LifeCyclePluginAnalyzer {
public static final String DEFAULTLIFECYCLEBINDINGS_MODELID = "org.apache.maven:maven-core:"
+ DefaultLifecyclePluginAnalyzer.class.getPackage().getImplementationVersion()
+ ":default-lifecycle-bindings";
private final Logger logger = LoggerFactory.getLogger(getClass());
private final Lookup lookup;
private final DefaultLifecycles defaultLifeCycles;
@Inject
public DefaultLifecyclePluginAnalyzer(Lookup lookup, DefaultLifecycles defaultLifeCycles) {
this.lookup = requireNonNull(lookup);
this.defaultLifeCycles = requireNonNull(defaultLifeCycles);
}
// These methods deal with construction intact Plugin object that look like they come from a standard
// <plugin/> block in a Maven POM. We have to do some wiggling to pull the sources of information
// together and this really shows the problem of constructing a sensible default configuration but
// it's all encapsulated here so it appears normalized to the POM builder.
// We are going to take the project packaging and find all plugins in the default lifecycle and create
// fully populated Plugin objects, including executions with goals and default configuration taken
// from the plugin.xml inside a plugin.
//
@Override
public Set<Plugin> getPluginsBoundByDefaultToAllLifecycles(String packaging) {
if (logger.isDebugEnabled()) {
logger.debug("Looking up lifecycle mappings for packaging " + packaging + " from "
+ Thread.currentThread().getContextClassLoader());
}
LifecycleMapping lifecycleMappingForPackaging = lookupLifecycleMapping(packaging);
if (lifecycleMappingForPackaging == null) {
return null;
}
Map<Plugin, Plugin> plugins = new LinkedHashMap<>();
for (Lifecycle lifecycle : defaultLifeCycles.getLifeCycles()) {
org.apache.maven.lifecycle.mapping.Lifecycle lifecycleConfiguration =
lifecycleMappingForPackaging.getLifecycles().get(lifecycle.getId());
Map<String, LifecyclePhase> phaseToGoalMapping = null;
if (lifecycleConfiguration != null) {
phaseToGoalMapping = lifecycleConfiguration.getLifecyclePhases();
} else if (lifecycle.getDefaultLifecyclePhases() != null) {
phaseToGoalMapping = lifecycle.getDefaultLifecyclePhases();
}
if (phaseToGoalMapping != null) {
for (Map.Entry<String, LifecyclePhase> goalsForLifecyclePhase : phaseToGoalMapping.entrySet()) {
String phase = goalsForLifecyclePhase.getKey();
LifecyclePhase goals = goalsForLifecyclePhase.getValue();
if (goals != null) {
parseLifecyclePhaseDefinitions(plugins, phase, goals);
}
}
}
}
return plugins.keySet();
}
/**
* Performs a lookup using Plexus API to make sure we can look up only "visible" (see Maven classloading) components
* from current module and for example not extensions coming from other modules.
*/
private LifecycleMapping lookupLifecycleMapping(final String packaging) {
return lookup.lookupOptional(LifecycleMapping.class, packaging).orElse(null);
}
private void parseLifecyclePhaseDefinitions(Map<Plugin, Plugin> plugins, String phase, LifecyclePhase goals) {
InputSource inputSource = new InputSource();
inputSource.setModelId(DEFAULTLIFECYCLEBINDINGS_MODELID);
InputLocation location = new InputLocation(-1, -1, inputSource);
location.setLocation(0, location);
List<LifecycleMojo> mojos = goals.getMojos();
if (mojos != null) {
for (int i = 0; i < mojos.size(); i++) {
LifecycleMojo mojo = mojos.get(i);
GoalSpec gs = parseGoalSpec(mojo.getGoal());
if (gs == null) {
logger.warn(
"Ignored invalid goal specification '{}' from lifecycle mapping for phase {}",
mojo.getGoal(),
phase);
continue;
}
Plugin plugin = new Plugin();
plugin.setGroupId(gs.groupId);
plugin.setArtifactId(gs.artifactId);
plugin.setVersion(gs.version);
plugin.setLocation("", location);
plugin.setLocation("groupId", location);
plugin.setLocation("artifactId", location);
plugin.setLocation("version", location);
Plugin existing = plugins.get(plugin);
if (existing != null) {
if (existing.getVersion() == null) {
existing.setVersion(plugin.getVersion());
existing.setLocation("version", location);
}
plugin = existing;
} else {
plugins.put(plugin, plugin);
}
PluginExecution execution = new PluginExecution();
execution.setId(getExecutionId(plugin, gs.goal));
execution.setPhase(phase);
execution.setPriority(i - mojos.size());
execution.getGoals().add(gs.goal);
execution.setLocation("", location);
execution.setLocation("id", location);
execution.setLocation("phase", location);
execution.setLocation("goals", location);
XmlNode lifecycleConfiguration = mojo.getConfiguration();
if (lifecycleConfiguration != null) {
execution.setConfiguration(new Xpp3Dom(lifecycleConfiguration));
}
if (mojo.getDependencies() != null) {
plugin.setDependencies(mojo.getDependencies());
}
plugin.getExecutions().add(execution);
}
}
}
private GoalSpec parseGoalSpec(String goalSpec) {
GoalSpec gs = new GoalSpec();
String[] p = goalSpec.trim().split(":");
if (p.length == 3) {
// <groupId>:<artifactId>:<goal>
gs.groupId = p[0];
gs.artifactId = p[1];
gs.goal = p[2];
} else if (p.length == 4) {
// <groupId>:<artifactId>:<version>:<goal>
gs.groupId = p[0];
gs.artifactId = p[1];
gs.version = p[2];
gs.goal = p[3];
} else {
// invalid
gs = null;
}
return gs;
}
private String getExecutionId(Plugin plugin, String goal) {
Set<String> existingIds = new HashSet<>();
for (PluginExecution execution : plugin.getExecutions()) {
existingIds.add(execution.getId());
}
String base = "default-" + goal;
String id = base;
for (int index = 1; existingIds.contains(id); index++) {
id = base + '-' + index;
}
return id;
}
static class GoalSpec {
String groupId;
String artifactId;
String version;
String goal;
}
}