001package org.apache.maven.lifecycle.internal;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *  http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.maven.lifecycle.DefaultLifecycles;
023import org.apache.maven.lifecycle.LifeCyclePluginAnalyzer;
024import org.apache.maven.lifecycle.Lifecycle;
025import org.apache.maven.lifecycle.mapping.LifecycleMapping;
026import org.apache.maven.model.Plugin;
027import org.apache.maven.model.PluginExecution;
028import org.codehaus.plexus.component.annotations.Component;
029import org.codehaus.plexus.component.annotations.Requirement;
030import org.codehaus.plexus.logging.Logger;
031import org.codehaus.plexus.util.StringUtils;
032
033import java.util.ArrayList;
034import java.util.Collections;
035import java.util.Comparator;
036import java.util.HashSet;
037import java.util.LinkedHashMap;
038import java.util.List;
039import java.util.Map;
040import java.util.Set;
041
042/**
043 * @since 3.0
044 * @author Benjamin Bentmann
045 * @author Jason van Zyl
046 * @author jdcasey
047 * @author Kristian Rosenvold (extracted class only)
048 *         <p/>
049 *         NOTE: This class is not part of any public api and can be changed or deleted without prior notice.
050 */
051@Component( role = LifeCyclePluginAnalyzer.class )
052public class DefaultLifecyclePluginAnalyzer
053    implements LifeCyclePluginAnalyzer
054{
055
056    @Requirement( role = LifecycleMapping.class )
057    private Map<String, LifecycleMapping> lifecycleMappings;
058
059    @Requirement
060    private DefaultLifecycles defaultLifeCycles;
061
062    @Requirement
063    private Logger logger;
064
065    public DefaultLifecyclePluginAnalyzer()
066    {
067    }
068
069    // These methods deal with construction intact Plugin object that look like they come from a standard
070    // <plugin/> block in a Maven POM. We have to do some wiggling to pull the sources of information
071    // together and this really shows the problem of constructing a sensible default configuration but
072    // it's all encapsulated here so it appears normalized to the POM builder.
073
074    // We are going to take the project packaging and find all plugin in the default lifecycle and create
075    // fully populated Plugin objects, including executions with goals and default configuration taken
076    // from the plugin.xml inside a plugin.
077    //
078
079    public Set<Plugin> getPluginsBoundByDefaultToAllLifecycles( String packaging )
080    {
081        if ( logger.isDebugEnabled() )
082        {
083            logger.debug( "Looking up lifecyle mappings for packaging " + packaging + " from "
084                + Thread.currentThread().getContextClassLoader() );
085        }
086
087        LifecycleMapping lifecycleMappingForPackaging = lifecycleMappings.get( packaging );
088
089        if ( lifecycleMappingForPackaging == null )
090        {
091            return null;
092        }
093
094        Map<Plugin, Plugin> plugins = new LinkedHashMap<Plugin, Plugin>();
095
096        for ( Lifecycle lifecycle : getOrderedLifecycles() )
097        {
098            org.apache.maven.lifecycle.mapping.Lifecycle lifecycleConfiguration =
099                lifecycleMappingForPackaging.getLifecycles().get( lifecycle.getId() );
100
101            Map<String, String> phaseToGoalMapping = null;
102
103            if ( lifecycleConfiguration != null )
104            {
105                phaseToGoalMapping = lifecycleConfiguration.getPhases();
106            }
107            else if ( lifecycle.getDefaultPhases() != null )
108            {
109                phaseToGoalMapping = lifecycle.getDefaultPhases();
110            }
111
112            if ( phaseToGoalMapping != null )
113            {
114                // These are of the form:
115                //
116                // compile -> org.apache.maven.plugins:maven-compiler-plugin:compile[,gid:aid:goal,...]
117                //
118                for ( Map.Entry<String, String> goalsForLifecyclePhase : phaseToGoalMapping.entrySet() )
119                {
120                    String phase = goalsForLifecyclePhase.getKey();
121                    String goals = goalsForLifecyclePhase.getValue();
122                    if ( goals != null )
123                    {
124                        parseLifecyclePhaseDefinitions( plugins, phase, goals );
125                    }
126                }
127            }
128        }
129
130        return plugins.keySet();
131    }
132
133    private List<Lifecycle> getOrderedLifecycles()
134    {
135        // NOTE: The lifecycle order can affect implied execution ids so we better be deterministic.
136
137        List<Lifecycle> lifecycles = new ArrayList<Lifecycle>( defaultLifeCycles.getLifeCycles() );
138
139        Collections.sort( lifecycles, new Comparator<Lifecycle>()
140        {
141
142            public int compare( Lifecycle l1, Lifecycle l2 )
143            {
144                return l1.getId().compareTo( l2.getId() );
145            }
146
147        } );
148
149        return lifecycles;
150    }
151
152    private void parseLifecyclePhaseDefinitions( Map<Plugin, Plugin> plugins, String phase, String goals )
153    {
154        String[] mojos = StringUtils.split( goals, "," );
155
156        for ( int i = 0; i < mojos.length; i++ )
157        {
158            GoalSpec gs = parseGoalSpec( mojos[i].trim() );
159
160            if ( gs == null )
161            {
162                logger.warn( "Ignored invalid goal specification '" + mojos[i] + "' from lifecycle mapping for phase "
163                    + phase );
164                continue;
165            }
166
167            Plugin plugin = new Plugin();
168            plugin.setGroupId( gs.groupId );
169            plugin.setArtifactId( gs.artifactId );
170            plugin.setVersion( gs.version );
171
172            Plugin existing = plugins.get( plugin );
173            if ( existing != null )
174            {
175                if ( existing.getVersion() == null )
176                {
177                    existing.setVersion( plugin.getVersion() );
178                }
179                plugin = existing;
180            }
181            else
182            {
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.length );
190            execution.getGoals().add( gs.goal );
191
192            plugin.getExecutions().add( execution );
193        }
194    }
195
196    private GoalSpec parseGoalSpec( String goalSpec )
197    {
198        GoalSpec gs = new GoalSpec();
199
200        String[] p = StringUtils.split( goalSpec.trim(), ":" );
201
202        if ( p.length == 3 )
203        {
204            // <groupId>:<artifactId>:<goal>
205            gs.groupId = p[0];
206            gs.artifactId = p[1];
207            gs.goal = p[2];
208        }
209        else if ( p.length == 4 )
210        {
211            // <groupId>:<artifactId>:<version>:<goal>
212            gs.groupId = p[0];
213            gs.artifactId = p[1];
214            gs.version = p[2];
215            gs.goal = p[3];
216        }
217        else
218        {
219            // invalid
220            gs = null;
221        }
222
223        return gs;
224    }
225
226    private String getExecutionId( Plugin plugin, String goal )
227    {
228        Set<String> existingIds = new HashSet<String>();
229        for ( PluginExecution execution : plugin.getExecutions() )
230        {
231            existingIds.add( execution.getId() );
232        }
233
234        String base = "default-" + goal;
235        String id = base;
236
237        for ( int index = 1; existingIds.contains( id ); index++ )
238        {
239            id = base + '-' + index;
240        }
241
242        return id;
243    }
244
245    static class GoalSpec
246    {
247
248        String groupId;
249
250        String artifactId;
251
252        String version;
253
254        String goal;
255
256    }
257
258}