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