View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.lifecycle.internal;
20  
21  import static java.util.Objects.requireNonNull;
22  
23  import java.util.HashSet;
24  import java.util.LinkedHashMap;
25  import java.util.List;
26  import java.util.Map;
27  import java.util.NoSuchElementException;
28  import java.util.Set;
29  import javax.inject.Inject;
30  import javax.inject.Named;
31  import javax.inject.Singleton;
32  import org.apache.maven.api.xml.Dom;
33  import org.apache.maven.lifecycle.DefaultLifecycles;
34  import org.apache.maven.lifecycle.LifeCyclePluginAnalyzer;
35  import org.apache.maven.lifecycle.Lifecycle;
36  import org.apache.maven.lifecycle.mapping.LifecycleMapping;
37  import org.apache.maven.lifecycle.mapping.LifecycleMojo;
38  import org.apache.maven.lifecycle.mapping.LifecyclePhase;
39  import org.apache.maven.model.InputLocation;
40  import org.apache.maven.model.InputSource;
41  import org.apache.maven.model.Plugin;
42  import org.apache.maven.model.PluginExecution;
43  import org.codehaus.plexus.PlexusContainer;
44  import org.codehaus.plexus.component.repository.exception.ComponentLookupException;
45  import org.codehaus.plexus.util.StringUtils;
46  import org.codehaus.plexus.util.xml.Xpp3Dom;
47  import org.slf4j.Logger;
48  import org.slf4j.LoggerFactory;
49  
50  /**
51   * <strong>NOTE:</strong> This class is not part of any public api and can be changed or deleted without prior notice.
52   *
53   * @since 3.0
54   * @author Benjamin Bentmann
55   * @author Jason van Zyl
56   * @author jdcasey
57   * @author Kristian Rosenvold (extracted class only)
58   */
59  @Singleton
60  @Named
61  public class DefaultLifecyclePluginAnalyzer implements LifeCyclePluginAnalyzer {
62      public static final String DEFAULTLIFECYCLEBINDINGS_MODELID = "org.apache.maven:maven-core:"
63              + DefaultLifecyclePluginAnalyzer.class.getPackage().getImplementationVersion()
64              + ":default-lifecycle-bindings";
65  
66      private final Logger logger = LoggerFactory.getLogger(getClass());
67  
68      private final PlexusContainer plexusContainer;
69  
70      private final DefaultLifecycles defaultLifeCycles;
71  
72      @Inject
73      public DefaultLifecyclePluginAnalyzer(
74              final PlexusContainer plexusContainer, final DefaultLifecycles defaultLifeCycles) {
75          this.plexusContainer = requireNonNull(plexusContainer);
76          this.defaultLifeCycles = requireNonNull(defaultLifeCycles);
77      }
78  
79      // These methods deal with construction intact Plugin object that look like they come from a standard
80      // <plugin/> block in a Maven POM. We have to do some wiggling to pull the sources of information
81      // together and this really shows the problem of constructing a sensible default configuration but
82      // it's all encapsulated here so it appears normalized to the POM builder.
83  
84      // We are going to take the project packaging and find all plugins in the default lifecycle and create
85      // fully populated Plugin objects, including executions with goals and default configuration taken
86      // from the plugin.xml inside a plugin.
87      //
88  
89      @Override
90      public Set<Plugin> getPluginsBoundByDefaultToAllLifecycles(String packaging) {
91          if (logger.isDebugEnabled()) {
92              logger.debug("Looking up lifecycle mappings for packaging " + packaging + " from "
93                      + Thread.currentThread().getContextClassLoader());
94          }
95  
96          LifecycleMapping lifecycleMappingForPackaging = lookupLifecycleMapping(packaging);
97  
98          if (lifecycleMappingForPackaging == null) {
99              return null;
100         }
101 
102         Map<Plugin, Plugin> plugins = new LinkedHashMap<>();
103 
104         for (Lifecycle lifecycle : defaultLifeCycles.getLifeCycles()) {
105             org.apache.maven.lifecycle.mapping.Lifecycle lifecycleConfiguration =
106                     lifecycleMappingForPackaging.getLifecycles().get(lifecycle.getId());
107 
108             Map<String, LifecyclePhase> phaseToGoalMapping = null;
109 
110             if (lifecycleConfiguration != null) {
111                 phaseToGoalMapping = lifecycleConfiguration.getLifecyclePhases();
112             } else if (lifecycle.getDefaultLifecyclePhases() != null) {
113                 phaseToGoalMapping = lifecycle.getDefaultLifecyclePhases();
114             }
115 
116             if (phaseToGoalMapping != null) {
117                 for (Map.Entry<String, LifecyclePhase> goalsForLifecyclePhase : phaseToGoalMapping.entrySet()) {
118                     String phase = goalsForLifecyclePhase.getKey();
119                     LifecyclePhase goals = goalsForLifecyclePhase.getValue();
120                     if (goals != null) {
121                         parseLifecyclePhaseDefinitions(plugins, phase, goals);
122                     }
123                 }
124             }
125         }
126 
127         return plugins.keySet();
128     }
129 
130     /**
131      * Performs a lookup using Plexus API to make sure we can look up only "visible" (see Maven classloading) components
132      * from current module and for example not extensions coming from other modules.
133      */
134     private LifecycleMapping lookupLifecycleMapping(final String packaging) {
135         try {
136             return plexusContainer.lookup(LifecycleMapping.class, packaging);
137         } catch (ComponentLookupException e) {
138             if (e.getCause() instanceof NoSuchElementException) {
139                 return null;
140             }
141             throw new RuntimeException(e);
142         }
143     }
144 
145     private void parseLifecyclePhaseDefinitions(Map<Plugin, Plugin> plugins, String phase, LifecyclePhase goals) {
146         InputSource inputSource = new InputSource();
147         inputSource.setModelId(DEFAULTLIFECYCLEBINDINGS_MODELID);
148         InputLocation location = new InputLocation(-1, -1, inputSource);
149         location.setLocation(0, location);
150 
151         List<LifecycleMojo> mojos = goals.getMojos();
152         if (mojos != null) {
153 
154             for (int i = 0; i < mojos.size(); i++) {
155                 LifecycleMojo mojo = mojos.get(i);
156 
157                 GoalSpec gs = parseGoalSpec(mojo.getGoal());
158 
159                 if (gs == null) {
160                     logger.warn("Ignored invalid goal specification '" + mojo.getGoal()
161                             + "' from lifecycle mapping for phase " + phase);
162                     continue;
163                 }
164 
165                 Plugin plugin = new Plugin();
166                 plugin.setGroupId(gs.groupId);
167                 plugin.setArtifactId(gs.artifactId);
168                 plugin.setVersion(gs.version);
169 
170                 plugin.setLocation("", location);
171                 plugin.setLocation("groupId", location);
172                 plugin.setLocation("artifactId", location);
173                 plugin.setLocation("version", location);
174 
175                 Plugin existing = plugins.get(plugin);
176                 if (existing != null) {
177                     if (existing.getVersion() == null) {
178                         existing.setVersion(plugin.getVersion());
179                         existing.setLocation("version", location);
180                     }
181                     plugin = existing;
182                 } else {
183                     plugins.put(plugin, plugin);
184                 }
185 
186                 PluginExecution execution = new PluginExecution();
187                 execution.setId(getExecutionId(plugin, gs.goal));
188                 execution.setPhase(phase);
189                 execution.setPriority(i - mojos.size());
190                 execution.getGoals().add(gs.goal);
191 
192                 execution.setLocation("", location);
193                 execution.setLocation("id", location);
194                 execution.setLocation("phase", location);
195                 execution.setLocation("goals", location);
196 
197                 Dom lifecycleConfiguration = mojo.getConfiguration();
198                 if (lifecycleConfiguration != null) {
199                     execution.setConfiguration(new Xpp3Dom(lifecycleConfiguration));
200                 }
201 
202                 if (mojo.getDependencies() != null) {
203                     plugin.setDependencies(mojo.getDependencies());
204                 }
205                 plugin.getExecutions().add(execution);
206             }
207         }
208     }
209 
210     private GoalSpec parseGoalSpec(String goalSpec) {
211         GoalSpec gs = new GoalSpec();
212 
213         String[] p = StringUtils.split(goalSpec.trim(), ":");
214 
215         if (p.length == 3) {
216             // <groupId>:<artifactId>:<goal>
217             gs.groupId = p[0];
218             gs.artifactId = p[1];
219             gs.goal = p[2];
220         } else if (p.length == 4) {
221             // <groupId>:<artifactId>:<version>:<goal>
222             gs.groupId = p[0];
223             gs.artifactId = p[1];
224             gs.version = p[2];
225             gs.goal = p[3];
226         } else {
227             // invalid
228             gs = null;
229         }
230 
231         return gs;
232     }
233 
234     private String getExecutionId(Plugin plugin, String goal) {
235         Set<String> existingIds = new HashSet<>();
236         for (PluginExecution execution : plugin.getExecutions()) {
237             existingIds.add(execution.getId());
238         }
239 
240         String base = "default-" + goal;
241         String id = base;
242 
243         for (int index = 1; existingIds.contains(id); index++) {
244             id = base + '-' + index;
245         }
246 
247         return id;
248     }
249 
250     static class GoalSpec {
251 
252         String groupId;
253 
254         String artifactId;
255 
256         String version;
257 
258         String goal;
259     }
260 }