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