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