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