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