001    package org.apache.maven.lifecycle;
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    
022    import java.util.ArrayList;
023    import java.util.HashMap;
024    import java.util.HashSet;
025    import java.util.Iterator;
026    import java.util.LinkedHashMap;
027    import java.util.LinkedHashSet;
028    import java.util.List;
029    import java.util.Map;
030    import java.util.Set;
031    
032    import org.apache.maven.lifecycle.internal.ExecutionPlanItem;
033    import org.apache.maven.model.Plugin;
034    import org.apache.maven.plugin.MojoExecution;
035    import org.apache.maven.plugin.descriptor.MojoDescriptor;
036    
037    //TODO: lifecycles being executed
038    //TODO: what runs in each phase
039    //TODO: plugins that need downloading
040    //TODO: project dependencies that need downloading
041    //TODO: unfortunately the plugins need to be downloaded in order to get the plugin.xml file. need to externalize this
042    //      from the plugin archive.
043    //TODO: this will be the class that people get in IDEs to modify
044    
045    public class MavenExecutionPlan
046        implements Iterable<ExecutionPlanItem>
047    {
048    
049        /*
050           At the moment, this class is totally immutable, and this is in line with thoughts about the
051           pre-calculated execution plan that stays the same during the execution.
052    
053           If deciding to add mutable state to this class, it should be at least considered to
054           separate this into a separate mutable structure.
055    
056         */
057    
058        private final List<ExecutionPlanItem> planItem;
059    
060        private final Map<String, ExecutionPlanItem> lastMojoExecutionForAllPhases;
061    
062        final List<String> phasesInExecutionPlan;
063    
064        public MavenExecutionPlan( List<ExecutionPlanItem> planItem, DefaultLifecycles defaultLifecycles )
065        {
066            this.planItem = planItem;
067    
068            lastMojoExecutionForAllPhases = new LinkedHashMap<String, ExecutionPlanItem>();
069    
070            LinkedHashSet<String> totalPhaseSet = new LinkedHashSet<String>();
071            if ( defaultLifecycles != null )
072            {
073                for ( String phase : getDistinctPhasesInOrderOfExecutionPlanAppearance( planItem ) )
074                {
075                    final Lifecycle lifecycle = defaultLifecycles.get( phase );
076                    if ( lifecycle != null )
077                    {
078                        totalPhaseSet.addAll( lifecycle.getPhases() );
079                    }
080                }
081            }
082            this.phasesInExecutionPlan = new ArrayList<String>( totalPhaseSet );
083    
084            Map<String, ExecutionPlanItem> lastInExistingPhases = new HashMap<String, ExecutionPlanItem>();
085            for ( ExecutionPlanItem executionPlanItem : getExecutionPlanItems() )
086            {
087                lastInExistingPhases.put( executionPlanItem.getLifecyclePhase(), executionPlanItem );
088            }
089    
090            ExecutionPlanItem lastSeenExecutionPlanItem = null;
091    
092            for ( String phase : totalPhaseSet )
093            {
094                ExecutionPlanItem forThisPhase = lastInExistingPhases.get( phase );
095                if ( forThisPhase != null )
096                {
097                    lastSeenExecutionPlanItem = forThisPhase;
098                }
099    
100                lastMojoExecutionForAllPhases.put( phase, lastSeenExecutionPlanItem );
101            }
102        }
103    
104        public Iterator<ExecutionPlanItem> iterator()
105        {
106            return getExecutionPlanItems().iterator();
107        }
108    
109        /**
110         * Returns the last ExecutionPlanItem in the supplied phase. If no items are in the specified phase,
111         * the closest executionPlanItem from an earlier phase item will be returned.
112         *
113         * @param requestedPhase the requested phase
114         *                       The execution plan item
115         * @return The ExecutionPlanItem or null if none can be found
116         */
117        public ExecutionPlanItem findLastInPhase( String requestedPhase )
118        {
119            return lastMojoExecutionForAllPhases.get( requestedPhase );
120        }
121    
122        private List<ExecutionPlanItem> getExecutionPlanItems()
123        {
124            return planItem;
125        }
126    
127        private static Iterable<String> getDistinctPhasesInOrderOfExecutionPlanAppearance(
128            List<ExecutionPlanItem> planItems )
129        {
130            LinkedHashSet<String> result = new LinkedHashSet<String>();
131            for ( ExecutionPlanItem executionPlanItem : planItems )
132            {
133                final String phase = executionPlanItem.getLifecyclePhase();
134                if ( !result.contains( phase ) )
135                {
136                    result.add( phase );
137                }
138            }
139            return result;
140        }
141    
142        public void forceAllComplete()
143        {
144            for ( ExecutionPlanItem executionPlanItem : getExecutionPlanItems() )
145            {
146                executionPlanItem.forceComplete();
147            }
148        }
149    
150        public void waitUntilAllDone()
151            throws InterruptedException
152        {
153            for ( ExecutionPlanItem executionPlanItem : getExecutionPlanItems() )
154            {
155                executionPlanItem.waitUntilDone();
156            }
157        }
158    
159        public boolean containsPhase( String phase )
160        {
161            return phasesInExecutionPlan.contains( phase );
162        }
163    
164        public List<MojoExecution> getMojoExecutions()
165        {
166            List<MojoExecution> result = new ArrayList<MojoExecution>();
167            for ( ExecutionPlanItem executionPlanItem : planItem )
168            {
169                result.add( executionPlanItem.getMojoExecution() );
170            }
171            return result;
172        }
173    
174        /**
175         * Get set of plugins having a goal/mojo used but not marked @threadSafe
176         *
177         * @return the set of plugins (without info on which goal is concerned)
178         */
179        public Set<Plugin> getNonThreadSafePlugins()
180        {
181            Set<Plugin> plugins = new HashSet<Plugin>();
182            for ( ExecutionPlanItem executionPlanItem : planItem )
183            {
184                final MojoExecution mojoExecution = executionPlanItem.getMojoExecution();
185                if ( !mojoExecution.getMojoDescriptor().isThreadSafe() )
186                {
187                    plugins.add( mojoExecution.getPlugin() );
188                }
189            }
190            return plugins;
191        }
192    
193        /**
194         * Get set of mojos used but not marked @threadSafe
195         *
196         * @return the set of mojo descriptors
197         */
198        public Set<MojoDescriptor> getNonThreadSafeMojos()
199        {
200            Set<MojoDescriptor> mojos = new HashSet<MojoDescriptor>();
201            for ( ExecutionPlanItem executionPlanItem : planItem )
202            {
203                final MojoExecution mojoExecution = executionPlanItem.getMojoExecution();
204                if ( !mojoExecution.getMojoDescriptor().isThreadSafe() )
205                {
206                    mojos.add( mojoExecution.getMojoDescriptor() );
207                }
208            }
209            return mojos;
210        }
211    
212        // Used by m2e but will be removed, really.
213        @Deprecated
214        public List<MojoExecution> getExecutions()
215        {
216            return getMojoExecutions();
217        }
218    
219        public int size()
220        {
221            return planItem.size();
222        }
223    
224    }