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