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        /**
253         * The map of artifacts accessible by the versionlessKey, i.e. groupId:artifactId
254         * 
255         * @return a Map of artifacts, never {@code null}
256         * @see #getArtifacts()
257         */
258        public Map<String, Artifact> getArtifactMap()
259        {
260            if ( artifactMap == null )
261            {
262                artifactMap = ArtifactUtils.artifactMapByVersionlessId( getArtifacts() );
263            }
264    
265            return artifactMap;
266        }
267    
268        public boolean equals( Object object )
269        {
270            if ( this == object )
271            {
272                return true;
273            }
274    
275            return getId().equals( ( (PluginDescriptor) object ).getId() );
276        }
277    
278        public int hashCode()
279        {
280            return 10 + getId().hashCode();
281        }
282    
283        public MojoDescriptor getMojo( String goal )
284        {
285            if ( getMojos() == null )
286            {
287                return null; // no mojo in this POM
288            }
289    
290            // TODO: could we use a map? Maybe if the parent did that for components too, as this is too vulnerable to
291            // changes above not being propagated to the map
292            for ( MojoDescriptor desc : getMojos() )
293            {
294                if ( goal.equals( desc.getGoal() ) )
295                {
296                    return desc;
297                }
298            }
299            return null;
300        }
301    
302        public void setClassRealm( ClassRealm classRealm )
303        {
304            this.classRealm = classRealm;
305        }
306    
307        public ClassRealm getClassRealm()
308        {
309            return classRealm;
310        }
311    
312        public void setIntroducedDependencyArtifacts( Set<Artifact> introducedDependencyArtifacts )
313        {
314            this.introducedDependencyArtifacts = introducedDependencyArtifacts;
315        }
316    
317        public Set<Artifact> getIntroducedDependencyArtifacts()
318        {
319            return ( introducedDependencyArtifacts != null ) ? introducedDependencyArtifacts
320                            : Collections.<Artifact> emptySet();
321        }
322    
323        public void setName( String name )
324        {
325            this.name = name;
326        }
327    
328        public String getName()
329        {
330            return name;
331        }
332    
333        public void setDescription( String description )
334        {
335            this.description = description;
336        }
337    
338        public String getDescription()
339        {
340            return description;
341        }
342    
343        public void setRequiredMavenVersion( String requiredMavenVersion )
344        {
345            this.requiredMavenVersion = requiredMavenVersion;
346        }
347    
348        public String getRequiredMavenVersion()
349        {
350            return requiredMavenVersion;
351        }
352    
353        public void setPlugin( Plugin plugin )
354        {
355            this.plugin = plugin;
356        }
357    
358        public Plugin getPlugin()
359        {
360            return plugin;
361        }
362    
363        public Artifact getPluginArtifact()
364        {
365            return pluginArtifact;
366        }
367    
368        public void setPluginArtifact( Artifact pluginArtifact )
369        {
370            this.pluginArtifact = pluginArtifact;
371        }
372    
373        public Lifecycle getLifecycleMapping( String lifecycleId )
374            throws IOException, XmlPullParserException
375        {
376            if ( lifecycleMappings == null )
377            {
378                LifecycleConfiguration lifecycleConfiguration;
379    
380                Reader reader = null;
381                try
382                {
383                    reader = ReaderFactory.newXmlReader( getDescriptorStream( LIFECYCLE_DESCRIPTOR ) );
384    
385                    lifecycleConfiguration = new LifecycleMappingsXpp3Reader().read( reader );
386                }
387                finally
388                {
389                    IOUtil.close( reader );
390                }
391    
392                lifecycleMappings = new HashMap<String, Lifecycle>();
393    
394                for ( Lifecycle lifecycle : lifecycleConfiguration.getLifecycles() )
395                {
396                    lifecycleMappings.put( lifecycle.getId(), lifecycle );
397                }
398            }
399    
400            return lifecycleMappings.get( lifecycleId );
401        }
402    
403        private InputStream getDescriptorStream( String descriptor )
404            throws IOException
405        {
406            File pluginFile = ( pluginArtifact != null ) ? pluginArtifact.getFile() : null;
407            if ( pluginFile == null )
408            {
409                throw new IllegalStateException( "plugin main artifact has not been resolved for " + getId() );
410            }
411    
412            if ( pluginFile.isFile() )
413            {
414                try
415                {
416                    return new URL( "jar:" + pluginFile.toURI() + "!/" + descriptor ).openStream();
417                }
418                catch ( MalformedURLException e )
419                {
420                    throw new IllegalStateException( e );
421                }
422            }
423            else
424            {
425                return new FileInputStream( new File( pluginFile, descriptor ) );
426            }
427        }
428    
429        /**
430         * Creates a shallow copy of this plugin descriptor.
431         */
432        @Override
433        public PluginDescriptor clone()
434        {
435            try
436            {
437                return (PluginDescriptor) super.clone();
438            }
439            catch ( CloneNotSupportedException e )
440            {
441                throw new UnsupportedOperationException( e );
442            }
443        }
444    
445    }