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 javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.util.ArrayList;
26  import java.util.LinkedHashMap;
27  import java.util.List;
28  import java.util.Map;
29  import java.util.TreeMap;
30  
31  import org.apache.maven.execution.MavenSession;
32  import org.apache.maven.lifecycle.Lifecycle;
33  import org.apache.maven.lifecycle.LifecycleMappingDelegate;
34  import org.apache.maven.model.Plugin;
35  import org.apache.maven.model.PluginExecution;
36  import org.apache.maven.plugin.BuildPluginManager;
37  import org.apache.maven.plugin.InvalidPluginDescriptorException;
38  import org.apache.maven.plugin.MojoExecution;
39  import org.apache.maven.plugin.MojoNotFoundException;
40  import org.apache.maven.plugin.PluginDescriptorParsingException;
41  import org.apache.maven.plugin.PluginNotFoundException;
42  import org.apache.maven.plugin.PluginResolutionException;
43  import org.apache.maven.plugin.descriptor.MojoDescriptor;
44  import org.apache.maven.project.MavenProject;
45  
46  /**
47   * Lifecycle mapping delegate component interface. Calculates project build execution plan given {@link Lifecycle} and
48   * lifecycle phase. Standard lifecycles use plugin execution {@code <phase>} or mojo default lifecycle phase to
49   * calculate the execution plan, but custom lifecycles can use alternative mapping strategies.
50   */
51  @Named(DefaultLifecycleMappingDelegate.HINT)
52  @Singleton
53  public class DefaultLifecycleMappingDelegate implements LifecycleMappingDelegate {
54      public static final String HINT = "default";
55  
56      private final BuildPluginManager pluginManager;
57  
58      @Inject
59      public DefaultLifecycleMappingDelegate(BuildPluginManager pluginManager) {
60          this.pluginManager = pluginManager;
61      }
62  
63      @Override
64      public Map<String, List<MojoExecution>> calculateLifecycleMappings(
65              MavenSession session, MavenProject project, Lifecycle lifecycle, String lifecyclePhase)
66              throws PluginNotFoundException, PluginResolutionException, PluginDescriptorParsingException,
67                      MojoNotFoundException, InvalidPluginDescriptorException {
68          /*
69           * Initialize mapping from lifecycle phase to bound mojos. The key set of this map denotes the phases the caller
70           * is interested in, i.e. all phases up to and including the specified phase.
71           */
72  
73          Map<String, Map<Integer, List<MojoExecution>>> mappings = new LinkedHashMap<>();
74  
75          for (String phase : lifecycle.getPhases()) {
76              Map<Integer, List<MojoExecution>> phaseBindings = new TreeMap<>();
77  
78              mappings.put(phase, phaseBindings);
79  
80              if (phase.equals(lifecyclePhase)) {
81                  break;
82              }
83          }
84  
85          /*
86           * Grab plugin executions that are bound to the selected lifecycle phases from project. The effective model of
87           * the project already contains the plugin executions induced by the project's packaging type. Remember, all
88           * phases of interest and only those are in the lifecycle mapping, if a phase has no value in the map, we are
89           * not interested in any of the executions bound to it.
90           */
91  
92          for (Plugin plugin : project.getBuild().getPlugins()) {
93              for (PluginExecution execution : plugin.getExecutions()) {
94                  // if the phase is specified then I don't have to go fetch the plugin yet and pull it down
95                  // to examine the phase it is associated to.
96                  if (execution.getPhase() != null) {
97                      Map<Integer, List<MojoExecution>> phaseBindings = mappings.get(execution.getPhase());
98                      if (phaseBindings != null) {
99                          for (String goal : execution.getGoals()) {
100                             MojoExecution mojoExecution = new MojoExecution(plugin, goal, execution.getId());
101                             mojoExecution.setLifecyclePhase(execution.getPhase());
102                             addMojoExecution(phaseBindings, mojoExecution, execution.getPriority());
103                         }
104                     }
105                 }
106                 // if not then I need to grab the mojo descriptor and look at the phase that is specified
107                 else {
108                     for (String goal : execution.getGoals()) {
109                         MojoDescriptor mojoDescriptor = pluginManager.getMojoDescriptor(
110                                 plugin, goal, project.getRemotePluginRepositories(), session.getRepositorySession());
111 
112                         Map<Integer, List<MojoExecution>> phaseBindings = mappings.get(mojoDescriptor.getPhase());
113                         if (phaseBindings != null) {
114                             MojoExecution mojoExecution = new MojoExecution(mojoDescriptor, execution.getId());
115                             mojoExecution.setLifecyclePhase(mojoDescriptor.getPhase());
116                             addMojoExecution(phaseBindings, mojoExecution, execution.getPriority());
117                         }
118                     }
119                 }
120             }
121         }
122 
123         Map<String, List<MojoExecution>> lifecycleMappings = new LinkedHashMap<>();
124 
125         for (Map.Entry<String, Map<Integer, List<MojoExecution>>> entry : mappings.entrySet()) {
126             List<MojoExecution> mojoExecutions = new ArrayList<>();
127 
128             for (List<MojoExecution> executions : entry.getValue().values()) {
129                 mojoExecutions.addAll(executions);
130             }
131 
132             lifecycleMappings.put(entry.getKey(), mojoExecutions);
133         }
134 
135         return lifecycleMappings;
136     }
137 
138     private void addMojoExecution(
139             Map<Integer, List<MojoExecution>> phaseBindings, MojoExecution mojoExecution, int priority) {
140         List<MojoExecution> mojoExecutions = phaseBindings.computeIfAbsent(priority, k -> new ArrayList<>());
141 
142         mojoExecutions.add(mojoExecution);
143     }
144 }