001    package org.apache.maven.plugin.descriptor;
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.io.File;
023    import java.io.FileInputStream;
024    import java.io.IOException;
025    import java.io.InputStream;
026    import java.io.Reader;
027    import java.net.MalformedURLException;
028    import java.net.URL;
029    import java.util.Collections;
030    import java.util.HashMap;
031    import java.util.List;
032    import java.util.Map;
033    import java.util.Set;
034    
035    import org.apache.maven.artifact.Artifact;
036    import org.apache.maven.artifact.ArtifactUtils;
037    import org.apache.maven.model.Plugin;
038    import org.apache.maven.plugin.lifecycle.Lifecycle;
039    import org.apache.maven.plugin.lifecycle.LifecycleConfiguration;
040    import org.apache.maven.plugin.lifecycle.io.xpp3.LifecycleMappingsXpp3Reader;
041    import org.codehaus.plexus.classworlds.realm.ClassRealm;
042    import org.codehaus.plexus.component.repository.ComponentSetDescriptor;
043    import org.codehaus.plexus.util.IOUtil;
044    import org.codehaus.plexus.util.ReaderFactory;
045    import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
046    
047    /**
048     * @author Jason van Zyl
049     */
050    public class PluginDescriptor
051        extends ComponentSetDescriptor
052        implements Cloneable
053    {
054    
055        private static final String LIFECYCLE_DESCRIPTOR = "META-INF/maven/lifecycle.xml";
056    
057        private String groupId;
058    
059        private String artifactId;
060    
061        private String version;
062    
063        private String goalPrefix;
064    
065        private String source;
066    
067        private boolean inheritedByDefault = true;
068    
069        private List<Artifact> artifacts;
070    
071        private ClassRealm classRealm;
072    
073        // calculated on-demand.
074        private Map<String, Artifact> artifactMap;
075    
076        private Set<Artifact> introducedDependencyArtifacts;
077    
078        private String name;
079    
080        private String description;
081    
082        private String requiredMavenVersion;
083    
084        private Plugin plugin;
085    
086        private Artifact pluginArtifact;
087    
088        private Map<String, Lifecycle> lifecycleMappings;
089    
090        // ----------------------------------------------------------------------
091        //
092        // ----------------------------------------------------------------------
093    
094        @SuppressWarnings( { "unchecked", "rawtypes" } )
095        public List<MojoDescriptor> getMojos()
096        {
097            return (List) getComponents();
098        }
099    
100        public void addMojo( MojoDescriptor mojoDescriptor )
101            throws DuplicateMojoDescriptorException
102        {
103            MojoDescriptor existing = null;
104            // this relies heavily on the equals() and hashCode() for ComponentDescriptor,
105            // which uses role:roleHint for identity...and roleHint == goalPrefix:goal.
106            // role does not vary for Mojos.
107            List<MojoDescriptor> mojos = getMojos();
108    
109            if ( mojos != null && mojos.contains( mojoDescriptor ) )
110            {
111                int indexOf = mojos.indexOf( mojoDescriptor );
112    
113                existing = mojos.get( indexOf );
114            }
115    
116            if ( existing != null )
117            {
118                throw new DuplicateMojoDescriptorException( getGoalPrefix(), mojoDescriptor.getGoal(), existing
119                    .getImplementation(), mojoDescriptor.getImplementation() );
120            }
121            else
122            {
123                addComponentDescriptor( mojoDescriptor );
124            }
125        }
126    
127        public String getGroupId()
128        {
129            return groupId;
130        }
131    
132        public void setGroupId( String groupId )
133        {
134            this.groupId = groupId;
135        }
136    
137        public String getArtifactId()
138        {
139            return artifactId;
140        }
141    
142        public void setArtifactId( String artifactId )
143        {
144            this.artifactId = artifactId;
145        }
146    
147        // ----------------------------------------------------------------------
148        // Dependencies
149        // ----------------------------------------------------------------------
150    
151        public static String constructPluginKey( String groupId, String artifactId, String version )
152        {
153            return groupId + ":" + artifactId + ":" + version;
154        }
155    
156        public String getPluginLookupKey()
157        {
158            return groupId + ":" + artifactId;
159        }
160    
161        public String getId()
162        {
163            return constructPluginKey( groupId, artifactId, version );
164        }
165    
166        public static String getDefaultPluginArtifactId( String id )
167        {
168            return "maven-" + id + "-plugin";
169        }
170    
171        public static String getDefaultPluginGroupId()
172        {
173            return "org.apache.maven.plugins";
174        }
175    
176        /**
177         * Parse maven-...-plugin.
178         *
179         * @todo move to plugin-tools-api as a default only
180         */
181        public static String getGoalPrefixFromArtifactId( String artifactId )
182        {
183            if ( "maven-plugin-plugin".equals( artifactId ) )
184            {
185                return "plugin";
186            }
187            else
188            {
189                return artifactId.replaceAll( "-?maven-?", "" ).replaceAll( "-?plugin-?", "" );
190            }
191        }
192    
193        public String getGoalPrefix()
194        {
195            return goalPrefix;
196        }
197    
198        public void setGoalPrefix( String goalPrefix )
199        {
200            this.goalPrefix = goalPrefix;
201        }
202    
203        public void setVersion( String version )
204        {
205            this.version = version;
206        }
207    
208        public String getVersion()
209        {
210            return version;
211        }
212    
213        public void setSource( String source )
214        {
215            this.source = source;
216        }
217    
218        public String getSource()
219        {
220            return source;
221        }
222    
223        public boolean isInheritedByDefault()
224        {
225            return inheritedByDefault;
226        }
227    
228        public void setInheritedByDefault( boolean inheritedByDefault )
229        {
230            this.inheritedByDefault = inheritedByDefault;
231        }
232    
233        /**
234         * Gets the artifacts that make up the plugin's class realm, excluding artifacts shadowed by the Maven core realm
235         * like {@code maven-project}.
236         *
237         * @return The plugin artifacts, never {@code null}.
238         */
239        public List<Artifact> getArtifacts()
240        {
241            return artifacts;
242        }
243    
244        public void setArtifacts( List<Artifact> artifacts )
245        {
246            this.artifacts = artifacts;
247    
248            // clear the calculated artifactMap
249            artifactMap = null;
250        }
251    
252        public Map<String, Artifact> getArtifactMap()
253        {
254            if ( artifactMap == null )
255            {
256                artifactMap = ArtifactUtils.artifactMapByVersionlessId( getArtifacts() );
257            }
258    
259            return artifactMap;
260        }
261    
262        public boolean equals( Object object )
263        {
264            if ( this == object )
265            {
266                return true;
267            }
268    
269            return getId().equals( ( (PluginDescriptor) object ).getId() );
270        }
271    
272        public int hashCode()
273        {
274            return 10 + getId().hashCode();
275        }
276    
277        public MojoDescriptor getMojo( String goal )
278        {
279            if ( getMojos() == null )
280            {
281                return null; // no mojo in this POM
282            }
283    
284            // TODO: could we use a map? Maybe if the parent did that for components too, as this is too vulnerable to
285            // changes above not being propagated to the map
286            for ( MojoDescriptor desc : getMojos() )
287            {
288                if ( goal.equals( desc.getGoal() ) )
289                {
290                    return desc;
291                }
292            }
293            return null;
294        }
295    
296        public void setClassRealm( ClassRealm classRealm )
297        {
298            this.classRealm = classRealm;
299        }
300    
301        public ClassRealm getClassRealm()
302        {
303            return classRealm;
304        }
305    
306        public void setIntroducedDependencyArtifacts( Set<Artifact> introducedDependencyArtifacts )
307        {
308            this.introducedDependencyArtifacts = introducedDependencyArtifacts;
309        }
310    
311        public Set<Artifact> getIntroducedDependencyArtifacts()
312        {
313            return ( introducedDependencyArtifacts != null ) ? introducedDependencyArtifacts
314                            : Collections.<Artifact> emptySet();
315        }
316    
317        public void setName( String name )
318        {
319            this.name = name;
320        }
321    
322        public String getName()
323        {
324            return name;
325        }
326    
327        public void setDescription( String description )
328        {
329            this.description = description;
330        }
331    
332        public String getDescription()
333        {
334            return description;
335        }
336    
337        public void setRequiredMavenVersion( String requiredMavenVersion )
338        {
339            this.requiredMavenVersion = requiredMavenVersion;
340        }
341    
342        public String getRequiredMavenVersion()
343        {
344            return requiredMavenVersion;
345        }
346    
347        public void setPlugin( Plugin plugin )
348        {
349            this.plugin = plugin;
350        }
351    
352        public Plugin getPlugin()
353        {
354            return plugin;
355        }
356    
357        public Artifact getPluginArtifact()
358        {
359            return pluginArtifact;
360        }
361    
362        public void setPluginArtifact( Artifact pluginArtifact )
363        {
364            this.pluginArtifact = pluginArtifact;
365        }
366    
367        public Lifecycle getLifecycleMapping( String lifecycleId )
368            throws IOException, XmlPullParserException
369        {
370            if ( lifecycleMappings == null )
371            {
372                LifecycleConfiguration lifecycleConfiguration;
373    
374                Reader reader = null;
375                try
376                {
377                    reader = ReaderFactory.newXmlReader( getDescriptorStream( LIFECYCLE_DESCRIPTOR ) );
378    
379                    lifecycleConfiguration = new LifecycleMappingsXpp3Reader().read( reader );
380                }
381                finally
382                {
383                    IOUtil.close( reader );
384                }
385    
386                lifecycleMappings = new HashMap<String, Lifecycle>();
387    
388                for ( Lifecycle lifecycle : lifecycleConfiguration.getLifecycles() )
389                {
390                    lifecycleMappings.put( lifecycle.getId(), lifecycle );
391                }
392            }
393    
394            return lifecycleMappings.get( lifecycleId );
395        }
396    
397        private InputStream getDescriptorStream( String descriptor )
398            throws IOException
399        {
400            File pluginFile = ( pluginArtifact != null ) ? pluginArtifact.getFile() : null;
401            if ( pluginFile == null )
402            {
403                throw new IllegalStateException( "plugin main artifact has not been resolved for " + getId() );
404            }
405    
406            if ( pluginFile.isFile() )
407            {
408                try
409                {
410                    return new URL( "jar:" + pluginFile.toURI() + "!/" + descriptor ).openStream();
411                }
412                catch ( MalformedURLException e )
413                {
414                    throw new IllegalStateException( e );
415                }
416            }
417            else
418            {
419                return new FileInputStream( new File( pluginFile, descriptor ) );
420            }
421        }
422    
423        /**
424         * Creates a shallow copy of this plugin descriptor.
425         */
426        @Override
427        public PluginDescriptor clone()
428        {
429            try
430            {
431                return (PluginDescriptor) super.clone();
432            }
433            catch ( CloneNotSupportedException e )
434            {
435                throw new UnsupportedOperationException( e );
436            }
437        }
438    
439    }