View Javadoc
1   package org.apache.maven.plugin.descriptor;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.artifact.Artifact;
23  import org.apache.maven.artifact.ArtifactUtils;
24  import org.apache.maven.model.Plugin;
25  import org.apache.maven.plugin.lifecycle.Lifecycle;
26  import org.apache.maven.plugin.lifecycle.LifecycleConfiguration;
27  import org.apache.maven.plugin.lifecycle.io.xpp3.LifecycleMappingsXpp3Reader;
28  import org.codehaus.plexus.classworlds.realm.ClassRealm;
29  import org.codehaus.plexus.component.repository.ComponentSetDescriptor;
30  import org.codehaus.plexus.util.ReaderFactory;
31  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
32  
33  import java.io.File;
34  import java.io.FileInputStream;
35  import java.io.IOException;
36  import java.io.InputStream;
37  import java.io.Reader;
38  import java.net.MalformedURLException;
39  import java.net.URL;
40  import java.util.Collections;
41  import java.util.HashMap;
42  import java.util.List;
43  import java.util.Map;
44  import java.util.Set;
45  import java.util.regex.Pattern;
46  
47  /**
48   * @author Jason van Zyl
49   */
50  public class PluginDescriptor
51      extends ComponentSetDescriptor
52      implements Cloneable
53  {
54  
55      private static final String LIFECYCLE_DESCRIPTOR = "META-INF/maven/lifecycle.xml";
56  
57      private static final Pattern PATTERN_FILTER_1 = Pattern.compile( "-?(maven|plugin)-?" );
58  
59      private String groupId;
60  
61      private String artifactId;
62  
63      private String version;
64  
65      private String goalPrefix;
66  
67      private String source;
68  
69      private boolean inheritedByDefault = true;
70  
71      private List<Artifact> artifacts;
72  
73      private ClassRealm classRealm;
74  
75      // calculated on-demand.
76      private Map<String, Artifact> artifactMap;
77  
78      private Set<Artifact> introducedDependencyArtifacts;
79  
80      private String name;
81  
82      private String description;
83  
84      private String requiredMavenVersion;
85  
86      private Plugin plugin;
87  
88      private Artifact pluginArtifact;
89  
90      private Map<String, Lifecycle> lifecycleMappings;
91  
92      // ----------------------------------------------------------------------
93      //
94      // ----------------------------------------------------------------------
95  
96      @SuppressWarnings( { "unchecked", "rawtypes" } )
97      public List<MojoDescriptor> getMojos()
98      {
99          return (List) getComponents();
100     }
101 
102     public void addMojo( MojoDescriptor mojoDescriptor )
103         throws DuplicateMojoDescriptorException
104     {
105         MojoDescriptor existing = null;
106         // this relies heavily on the equals() and hashCode() for ComponentDescriptor,
107         // which uses role:roleHint for identity...and roleHint == goalPrefix:goal.
108         // role does not vary for Mojos.
109         List<MojoDescriptor> mojos = getMojos();
110 
111         if ( mojos != null && mojos.contains( mojoDescriptor ) )
112         {
113             int indexOf = mojos.indexOf( mojoDescriptor );
114 
115             existing = mojos.get( indexOf );
116         }
117 
118         if ( existing != null )
119         {
120             throw new DuplicateMojoDescriptorException( getGoalPrefix(), mojoDescriptor.getGoal(),
121                                                         existing.getImplementation(),
122                                                         mojoDescriptor.getImplementation() );
123         }
124         else
125         {
126             addComponentDescriptor( mojoDescriptor );
127         }
128     }
129 
130     public String getGroupId()
131     {
132         return groupId;
133     }
134 
135     public void setGroupId( String groupId )
136     {
137         this.groupId = groupId;
138     }
139 
140     public String getArtifactId()
141     {
142         return artifactId;
143     }
144 
145     public void setArtifactId( String artifactId )
146     {
147         this.artifactId = artifactId;
148     }
149 
150     // ----------------------------------------------------------------------
151     // Dependencies
152     // ----------------------------------------------------------------------
153 
154     public static String constructPluginKey( String groupId, String artifactId, String version )
155     {
156         return groupId + ":" + artifactId + ":" + version;
157     }
158 
159     public String getPluginLookupKey()
160     {
161         return groupId + ":" + artifactId;
162     }
163 
164     public String getId()
165     {
166         return constructPluginKey( groupId, artifactId, version );
167     }
168 
169     public static String getDefaultPluginArtifactId( String id )
170     {
171         return "maven-" + id + "-plugin";
172     }
173 
174     public static String getDefaultPluginGroupId()
175     {
176         return "org.apache.maven.plugins";
177     }
178 
179     /**
180      * Parse maven-...-plugin.
181      *
182      * TODO move to plugin-tools-api as a default only
183      */
184     public static String getGoalPrefixFromArtifactId( String artifactId )
185     {
186         if ( "maven-plugin-plugin".equals( artifactId ) )
187         {
188             return "plugin";
189         }
190         else
191         {
192             return PATTERN_FILTER_1.matcher( artifactId ).replaceAll( "" );
193         }
194     }
195 
196     public String getGoalPrefix()
197     {
198         return goalPrefix;
199     }
200 
201     public void setGoalPrefix( String goalPrefix )
202     {
203         this.goalPrefix = goalPrefix;
204     }
205 
206     public void setVersion( String version )
207     {
208         this.version = version;
209     }
210 
211     public String getVersion()
212     {
213         return version;
214     }
215 
216     public void setSource( String source )
217     {
218         this.source = source;
219     }
220 
221     public String getSource()
222     {
223         return source;
224     }
225 
226     public boolean isInheritedByDefault()
227     {
228         return inheritedByDefault;
229     }
230 
231     public void setInheritedByDefault( boolean inheritedByDefault )
232     {
233         this.inheritedByDefault = inheritedByDefault;
234     }
235 
236     /**
237      * Gets the artifacts that make up the plugin's class realm, excluding artifacts shadowed by the Maven core realm
238      * like {@code maven-project}.
239      *
240      * @return The plugin artifacts, never {@code null}.
241      */
242     public List<Artifact> getArtifacts()
243     {
244         return artifacts;
245     }
246 
247     public void setArtifacts( List<Artifact> artifacts )
248     {
249         this.artifacts = artifacts;
250 
251         // clear the calculated artifactMap
252         artifactMap = null;
253     }
254 
255     /**
256      * The map of artifacts accessible by the versionlessKey, i.e. groupId:artifactId
257      *
258      * @return a Map of artifacts, never {@code null}
259      * @see #getArtifacts()
260      */
261     public Map<String, Artifact> getArtifactMap()
262     {
263         if ( artifactMap == null )
264         {
265             artifactMap = ArtifactUtils.artifactMapByVersionlessId( getArtifacts() );
266         }
267 
268         return artifactMap;
269     }
270 
271     public boolean equals( Object object )
272     {
273         if ( this == object )
274         {
275             return true;
276         }
277 
278         return object instanceof PluginDescriptor && getId().equals( ( (PluginDescriptor) object ).getId() );
279     }
280 
281     public int hashCode()
282     {
283         return 10 + getId().hashCode();
284     }
285 
286     public MojoDescriptor getMojo( String goal )
287     {
288         if ( getMojos() == null )
289         {
290             return null; // no mojo in this POM
291         }
292 
293         // TODO could we use a map? Maybe if the parent did that for components too, as this is too vulnerable to
294         // changes above not being propagated to the map
295         for ( MojoDescriptor desc : getMojos() )
296         {
297             if ( goal.equals( desc.getGoal() ) )
298             {
299                 return desc;
300             }
301         }
302         return null;
303     }
304 
305     public void setClassRealm( ClassRealm classRealm )
306     {
307         this.classRealm = classRealm;
308     }
309 
310     public ClassRealm getClassRealm()
311     {
312         return classRealm;
313     }
314 
315     public void setIntroducedDependencyArtifacts( Set<Artifact> introducedDependencyArtifacts )
316     {
317         this.introducedDependencyArtifacts = introducedDependencyArtifacts;
318     }
319 
320     public Set<Artifact> getIntroducedDependencyArtifacts()
321     {
322         return ( introducedDependencyArtifacts != null )
323             ? introducedDependencyArtifacts
324             : Collections.<Artifact>emptySet();
325     }
326 
327     public void setName( String name )
328     {
329         this.name = name;
330     }
331 
332     public String getName()
333     {
334         return name;
335     }
336 
337     public void setDescription( String description )
338     {
339         this.description = description;
340     }
341 
342     public String getDescription()
343     {
344         return description;
345     }
346 
347     public void setRequiredMavenVersion( String requiredMavenVersion )
348     {
349         this.requiredMavenVersion = requiredMavenVersion;
350     }
351 
352     public String getRequiredMavenVersion()
353     {
354         return requiredMavenVersion;
355     }
356 
357     public void setPlugin( Plugin plugin )
358     {
359         this.plugin = plugin;
360     }
361 
362     public Plugin getPlugin()
363     {
364         return plugin;
365     }
366 
367     public Artifact getPluginArtifact()
368     {
369         return pluginArtifact;
370     }
371 
372     public void setPluginArtifact( Artifact pluginArtifact )
373     {
374         this.pluginArtifact = pluginArtifact;
375     }
376 
377     public Lifecycle getLifecycleMapping( String lifecycleId )
378         throws IOException, XmlPullParserException
379     {
380         if ( lifecycleMappings == null )
381         {
382             LifecycleConfiguration lifecycleConfiguration;
383 
384             try ( Reader reader = ReaderFactory.newXmlReader( getDescriptorStream( LIFECYCLE_DESCRIPTOR ) ) )
385             {
386                 lifecycleConfiguration = new LifecycleMappingsXpp3Reader().read( reader );
387             }
388 
389             lifecycleMappings = new HashMap<>();
390 
391             for ( Lifecycle lifecycle : lifecycleConfiguration.getLifecycles() )
392             {
393                 lifecycleMappings.put( lifecycle.getId(), lifecycle );
394             }
395         }
396 
397         return lifecycleMappings.get( lifecycleId );
398     }
399 
400     private InputStream getDescriptorStream( String descriptor )
401         throws IOException
402     {
403         File pluginFile = ( pluginArtifact != null ) ? pluginArtifact.getFile() : null;
404         if ( pluginFile == null )
405         {
406             throw new IllegalStateException( "plugin main artifact has not been resolved for " + getId() );
407         }
408 
409         if ( pluginFile.isFile() )
410         {
411             try
412             {
413                 return new URL( "jar:" + pluginFile.toURI() + "!/" + descriptor ).openStream();
414             }
415             catch ( MalformedURLException e )
416             {
417                 throw new IllegalStateException( e );
418             }
419         }
420         else
421         {
422             return new FileInputStream( new File( pluginFile, descriptor ) );
423         }
424     }
425 
426     /**
427      * Creates a shallow copy of this plugin descriptor.
428      */
429     @Override
430     public PluginDescriptor clone()
431     {
432         try
433         {
434             return (PluginDescriptor) super.clone();
435         }
436         catch ( CloneNotSupportedException e )
437         {
438             throw new UnsupportedOperationException( e );
439         }
440     }
441 
442 }