001package 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
022import org.apache.maven.artifact.Artifact;
023import org.apache.maven.artifact.ArtifactUtils;
024import org.apache.maven.model.Plugin;
025import org.apache.maven.plugin.lifecycle.Lifecycle;
026import org.apache.maven.plugin.lifecycle.LifecycleConfiguration;
027import org.apache.maven.plugin.lifecycle.io.xpp3.LifecycleMappingsXpp3Reader;
028import org.codehaus.plexus.classworlds.realm.ClassRealm;
029import org.codehaus.plexus.component.repository.ComponentSetDescriptor;
030import org.codehaus.plexus.util.ReaderFactory;
031import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
032
033import java.io.File;
034import java.io.FileInputStream;
035import java.io.IOException;
036import java.io.InputStream;
037import java.io.Reader;
038import java.net.MalformedURLException;
039import java.net.URL;
040import java.util.Collections;
041import java.util.HashMap;
042import java.util.List;
043import java.util.Map;
044import java.util.Set;
045
046/**
047 * @author Jason van Zyl
048 */
049public class PluginDescriptor
050    extends ComponentSetDescriptor
051    implements Cloneable
052{
053
054    private static final String LIFECYCLE_DESCRIPTOR = "META-INF/maven/lifecycle.xml";
055
056    private String groupId;
057
058    private String artifactId;
059
060    private String version;
061
062    private String goalPrefix;
063
064    private String source;
065
066    private boolean inheritedByDefault = true;
067
068    private List<Artifact> artifacts;
069
070    private ClassRealm classRealm;
071
072    // calculated on-demand.
073    private Map<String, Artifact> artifactMap;
074
075    private Set<Artifact> introducedDependencyArtifacts;
076
077    private String name;
078
079    private String description;
080
081    private String requiredMavenVersion;
082
083    private Plugin plugin;
084
085    private Artifact pluginArtifact;
086
087    private Map<String, Lifecycle> lifecycleMappings;
088
089    // ----------------------------------------------------------------------
090    //
091    // ----------------------------------------------------------------------
092
093    @SuppressWarnings( { "unchecked", "rawtypes" } )
094    public List<MojoDescriptor> getMojos()
095    {
096        return (List) getComponents();
097    }
098
099    public void addMojo( MojoDescriptor mojoDescriptor )
100        throws DuplicateMojoDescriptorException
101    {
102        MojoDescriptor existing = null;
103        // this relies heavily on the equals() and hashCode() for ComponentDescriptor,
104        // which uses role:roleHint for identity...and roleHint == goalPrefix:goal.
105        // role does not vary for Mojos.
106        List<MojoDescriptor> mojos = getMojos();
107
108        if ( mojos != null && mojos.contains( mojoDescriptor ) )
109        {
110            int indexOf = mojos.indexOf( mojoDescriptor );
111
112            existing = mojos.get( indexOf );
113        }
114
115        if ( existing != null )
116        {
117            throw new DuplicateMojoDescriptorException( getGoalPrefix(), mojoDescriptor.getGoal(),
118                                                        existing.getImplementation(),
119                                                        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 object instanceof PluginDescriptor && 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 )
320            ? introducedDependencyArtifacts
321            : Collections.<Artifact>emptySet();
322    }
323
324    public void setName( String name )
325    {
326        this.name = name;
327    }
328
329    public String getName()
330    {
331        return name;
332    }
333
334    public void setDescription( String description )
335    {
336        this.description = description;
337    }
338
339    public String getDescription()
340    {
341        return description;
342    }
343
344    public void setRequiredMavenVersion( String requiredMavenVersion )
345    {
346        this.requiredMavenVersion = requiredMavenVersion;
347    }
348
349    public String getRequiredMavenVersion()
350    {
351        return requiredMavenVersion;
352    }
353
354    public void setPlugin( Plugin plugin )
355    {
356        this.plugin = plugin;
357    }
358
359    public Plugin getPlugin()
360    {
361        return plugin;
362    }
363
364    public Artifact getPluginArtifact()
365    {
366        return pluginArtifact;
367    }
368
369    public void setPluginArtifact( Artifact pluginArtifact )
370    {
371        this.pluginArtifact = pluginArtifact;
372    }
373
374    public Lifecycle getLifecycleMapping( String lifecycleId )
375        throws IOException, XmlPullParserException
376    {
377        if ( lifecycleMappings == null )
378        {
379            LifecycleConfiguration lifecycleConfiguration;
380
381            try ( Reader reader = ReaderFactory.newXmlReader( getDescriptorStream( LIFECYCLE_DESCRIPTOR ) ) )
382            {
383                lifecycleConfiguration = new LifecycleMappingsXpp3Reader().read( reader );
384            }
385
386            lifecycleMappings = new HashMap<>();
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}