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 String requiredJavaVersion;
84  
85      private Plugin plugin;
86  
87      private Artifact pluginArtifact;
88  
89      private Map<String, Lifecycle> lifecycleMappings;
90  
91      // ----------------------------------------------------------------------
92      //
93      // ----------------------------------------------------------------------
94  
95      @SuppressWarnings({"unchecked", "rawtypes"})
96      public List<MojoDescriptor> getMojos() {
97          return (List) getComponents();
98      }
99  
100     public void addMojo(MojoDescriptor mojoDescriptor) throws DuplicateMojoDescriptorException {
101         MojoDescriptor existing = null;
102         // this relies heavily on the equals() and hashCode() for ComponentDescriptor,
103         // which uses role:roleHint for identity...and roleHint == goalPrefix:goal.
104         // role does not vary for Mojos.
105         List<MojoDescriptor> mojos = getMojos();
106 
107         if (mojos != null && mojos.contains(mojoDescriptor)) {
108             int indexOf = mojos.indexOf(mojoDescriptor);
109 
110             existing = mojos.get(indexOf);
111         }
112 
113         if (existing != null) {
114             throw new DuplicateMojoDescriptorException(
115                     getGoalPrefix(),
116                     mojoDescriptor.getGoal(),
117                     existing.getImplementation(),
118                     mojoDescriptor.getImplementation());
119         } else {
120             addComponentDescriptor(mojoDescriptor);
121         }
122     }
123 
124     public String getGroupId() {
125         return groupId;
126     }
127 
128     public void setGroupId(String groupId) {
129         this.groupId = groupId;
130     }
131 
132     public String getArtifactId() {
133         return artifactId;
134     }
135 
136     public void setArtifactId(String artifactId) {
137         this.artifactId = artifactId;
138     }
139 
140     // ----------------------------------------------------------------------
141     // Dependencies
142     // ----------------------------------------------------------------------
143 
144     public static String constructPluginKey(String groupId, String artifactId, String version) {
145         return groupId + ":" + artifactId + ":" + version;
146     }
147 
148     public String getPluginLookupKey() {
149         return groupId + ":" + artifactId;
150     }
151 
152     public String getId() {
153         return constructPluginKey(groupId, artifactId, version);
154     }
155 
156     public static String getDefaultPluginArtifactId(String id) {
157         return "maven-" + id + "-plugin";
158     }
159 
160     public static String getDefaultPluginGroupId() {
161         return "org.apache.maven.plugins";
162     }
163 
164     /**
165      * Parse maven-...-plugin.
166      *
167      * TODO move to plugin-tools-api as a default only
168      */
169     public static String getGoalPrefixFromArtifactId(String artifactId) {
170         if ("maven-plugin-plugin".equals(artifactId)) {
171             return "plugin";
172         } else {
173             return PATTERN_FILTER_1.matcher(artifactId).replaceAll("");
174         }
175     }
176 
177     public String getGoalPrefix() {
178         return goalPrefix;
179     }
180 
181     public void setGoalPrefix(String goalPrefix) {
182         this.goalPrefix = goalPrefix;
183     }
184 
185     public void setVersion(String version) {
186         this.version = version;
187     }
188 
189     public String getVersion() {
190         return version;
191     }
192 
193     public void setSource(String source) {
194         this.source = source;
195     }
196 
197     public String getSource() {
198         return source;
199     }
200 
201     public boolean isInheritedByDefault() {
202         return inheritedByDefault;
203     }
204 
205     public void setInheritedByDefault(boolean inheritedByDefault) {
206         this.inheritedByDefault = inheritedByDefault;
207     }
208 
209     /**
210      * Gets the artifacts that make up the plugin's class realm, excluding artifacts shadowed by the Maven core realm
211      * like {@code maven-project}.
212      *
213      * @return The plugin artifacts, never {@code null}.
214      */
215     public List<Artifact> getArtifacts() {
216         return artifacts;
217     }
218 
219     public void setArtifacts(List<Artifact> artifacts) {
220         this.artifacts = artifacts;
221 
222         // clear the calculated artifactMap
223         artifactMap = null;
224     }
225 
226     /**
227      * The map of artifacts accessible by the versionlessKey, i.e. groupId:artifactId
228      *
229      * @return a Map of artifacts, never {@code null}
230      * @see #getArtifacts()
231      */
232     public Map<String, Artifact> getArtifactMap() {
233         if (artifactMap == null) {
234             artifactMap = ArtifactUtils.artifactMapByVersionlessId(getArtifacts());
235         }
236 
237         return artifactMap;
238     }
239 
240     public boolean equals(Object object) {
241         if (this == object) {
242             return true;
243         }
244 
245         return object instanceof PluginDescriptor && getId().equals(((PluginDescriptor) object).getId());
246     }
247 
248     public int hashCode() {
249         return 10 + getId().hashCode();
250     }
251 
252     public MojoDescriptor getMojo(String goal) {
253         if (getMojos() == null) {
254             return null; // no mojo in this POM
255         }
256 
257         // TODO could we use a map? Maybe if the parent did that for components too, as this is too vulnerable to
258         // changes above not being propagated to the map
259         for (MojoDescriptor desc : getMojos()) {
260             if (goal.equals(desc.getGoal())) {
261                 return desc;
262             }
263         }
264         return null;
265     }
266 
267     public void setClassRealm(ClassRealm classRealm) {
268         this.classRealm = classRealm;
269     }
270 
271     public ClassRealm getClassRealm() {
272         return classRealm;
273     }
274 
275     public void setIntroducedDependencyArtifacts(Set<Artifact> introducedDependencyArtifacts) {
276         this.introducedDependencyArtifacts = introducedDependencyArtifacts;
277     }
278 
279     public Set<Artifact> getIntroducedDependencyArtifacts() {
280         return (introducedDependencyArtifacts != null)
281                 ? introducedDependencyArtifacts
282                 : Collections.<Artifact>emptySet();
283     }
284 
285     public void setName(String name) {
286         this.name = name;
287     }
288 
289     public String getName() {
290         return name;
291     }
292 
293     public void setDescription(String description) {
294         this.description = description;
295     }
296 
297     public String getDescription() {
298         return description;
299     }
300 
301     /**
302      * Set required Maven version, as defined in plugin's pom.xml (not plugin.xml).
303      *
304      * @param requiredMavenVersion Maven version required by the plugin
305      * @since 3.0.2
306      */
307     // used by maven-core's org.apache.maven.plugin.internal.DefaultMavenPluginManager#getPluginDescriptor(...)
308     // but NOT PluginDescriptorBuilder
309     public void setRequiredMavenVersion(String requiredMavenVersion) {
310         this.requiredMavenVersion = requiredMavenVersion;
311     }
312 
313     /**
314      * Get required Maven version, as defined in plugin's pom.xml (not plugin.xml).
315      *
316      * @return the Maven version required by the plugin
317      * @since 3.0.2
318      */
319     public String getRequiredMavenVersion() {
320         return requiredMavenVersion;
321     }
322 
323     public void setRequiredJavaVersion(String requiredJavaVersion) {
324         this.requiredJavaVersion = requiredJavaVersion;
325     }
326 
327     public String getRequiredJavaVersion() {
328         return requiredJavaVersion;
329     }
330 
331     public void setPlugin(Plugin plugin) {
332         this.plugin = plugin;
333     }
334 
335     public Plugin getPlugin() {
336         return plugin;
337     }
338 
339     public Artifact getPluginArtifact() {
340         return pluginArtifact;
341     }
342 
343     public void setPluginArtifact(Artifact pluginArtifact) {
344         this.pluginArtifact = pluginArtifact;
345     }
346 
347     public Lifecycle getLifecycleMapping(String lifecycleId) throws IOException, XmlPullParserException {
348         if (lifecycleMappings == null) {
349             LifecycleConfiguration lifecycleConfiguration;
350 
351             try (Reader reader = ReaderFactory.newXmlReader(getDescriptorStream(LIFECYCLE_DESCRIPTOR))) {
352                 lifecycleConfiguration = new LifecycleMappingsXpp3Reader().read(reader);
353             }
354 
355             lifecycleMappings = new HashMap<>();
356 
357             for (Lifecycle lifecycle : lifecycleConfiguration.getLifecycles()) {
358                 lifecycleMappings.put(lifecycle.getId(), lifecycle);
359             }
360         }
361 
362         return lifecycleMappings.get(lifecycleId);
363     }
364 
365     private InputStream getDescriptorStream(String descriptor) throws IOException {
366         File pluginFile = (pluginArtifact != null) ? pluginArtifact.getFile() : null;
367         if (pluginFile == null) {
368             throw new IllegalStateException("plugin main artifact has not been resolved for " + getId());
369         }
370 
371         if (pluginFile.isFile()) {
372             try {
373                 return new URL("jar:" + pluginFile.toURI() + "!/" + descriptor).openStream();
374             } catch (MalformedURLException e) {
375                 throw new IllegalStateException(e);
376             }
377         } else {
378             return new FileInputStream(new File(pluginFile, descriptor));
379         }
380     }
381 
382     /**
383      * Creates a shallow copy of this plugin descriptor.
384      */
385     @Override
386     public PluginDescriptor clone() {
387         try {
388             return (PluginDescriptor) super.clone();
389         } catch (CloneNotSupportedException e) {
390             throw new UnsupportedOperationException(e);
391         }
392     }
393 }