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 javax.xml.stream.XMLStreamException;
22  
23  import java.io.File;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.net.MalformedURLException;
27  import java.net.URL;
28  import java.nio.file.Files;
29  import java.util.Collections;
30  import java.util.HashMap;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Set;
34  import java.util.regex.Pattern;
35  
36  import org.apache.maven.artifact.Artifact;
37  import org.apache.maven.artifact.ArtifactUtils;
38  import org.apache.maven.model.Plugin;
39  import org.apache.maven.plugin.lifecycle.Lifecycle;
40  import org.apache.maven.plugin.lifecycle.LifecycleConfiguration;
41  import org.apache.maven.plugin.lifecycle.io.LifecycleMappingsStaxReader;
42  import org.codehaus.plexus.classworlds.realm.ClassRealm;
43  import org.codehaus.plexus.component.repository.ComponentSetDescriptor;
44  
45  /**
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 static final Pattern PATTERN_FILTER_1 = Pattern.compile("-?(maven|plugin)-?");
52  
53      private String groupId;
54  
55      private String artifactId;
56  
57      private String version;
58  
59      private String goalPrefix;
60  
61      private String source;
62  
63      private boolean inheritedByDefault = true;
64  
65      private List<Artifact> artifacts;
66  
67      private ClassRealm classRealm;
68  
69      // calculated on-demand.
70      private Map<String, Artifact> artifactMap;
71  
72      private Set<Artifact> introducedDependencyArtifacts;
73  
74      private String name;
75  
76      private String description;
77  
78      private String requiredMavenVersion;
79  
80      private String requiredJavaVersion;
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) ? introducedDependencyArtifacts : Collections.emptySet();
278     }
279 
280     public void setName(String name) {
281         this.name = name;
282     }
283 
284     public String getName() {
285         return name;
286     }
287 
288     public void setDescription(String description) {
289         this.description = description;
290     }
291 
292     public String getDescription() {
293         return description;
294     }
295 
296     public void setRequiredMavenVersion(String requiredMavenVersion) {
297         this.requiredMavenVersion = requiredMavenVersion;
298     }
299 
300     public String getRequiredMavenVersion() {
301         return requiredMavenVersion;
302     }
303 
304     public void setRequiredJavaVersion(String requiredJavaVersion) {
305         this.requiredJavaVersion = requiredJavaVersion;
306     }
307 
308     public String getRequiredJavaVersion() {
309         return requiredJavaVersion;
310     }
311 
312     public void setPlugin(Plugin plugin) {
313         this.plugin = plugin;
314     }
315 
316     public Plugin getPlugin() {
317         return plugin;
318     }
319 
320     public Artifact getPluginArtifact() {
321         return pluginArtifact;
322     }
323 
324     public void setPluginArtifact(Artifact pluginArtifact) {
325         this.pluginArtifact = pluginArtifact;
326     }
327 
328     public Lifecycle getLifecycleMapping(String lifecycleId) throws IOException, XMLStreamException {
329         if (lifecycleMappings == null) {
330             LifecycleConfiguration lifecycleConfiguration;
331 
332             try (InputStream input = getDescriptorStream(LIFECYCLE_DESCRIPTOR)) {
333                 lifecycleConfiguration = new LifecycleMappingsStaxReader().read(input);
334             }
335 
336             lifecycleMappings = new HashMap<>();
337 
338             for (Lifecycle lifecycle : lifecycleConfiguration.getLifecycles()) {
339                 lifecycleMappings.put(lifecycle.getId(), lifecycle);
340             }
341         }
342 
343         return lifecycleMappings.get(lifecycleId);
344     }
345 
346     private InputStream getDescriptorStream(String descriptor) throws IOException {
347         File pluginFile = (pluginArtifact != null) ? pluginArtifact.getFile() : null;
348         if (pluginFile == null) {
349             throw new IllegalStateException("plugin main artifact has not been resolved for " + getId());
350         }
351 
352         if (pluginFile.isFile()) {
353             try {
354                 return new URL("jar:" + pluginFile.toURI() + "!/" + descriptor).openStream();
355             } catch (MalformedURLException e) {
356                 throw new IllegalStateException(e);
357             }
358         } else {
359             return Files.newInputStream(new File(pluginFile, descriptor).toPath());
360         }
361     }
362 
363     /**
364      * Creates a shallow copy of this plugin descriptor.
365      */
366     @Override
367     public PluginDescriptor clone() {
368         try {
369             return (PluginDescriptor) super.clone();
370         } catch (CloneNotSupportedException e) {
371             throw new UnsupportedOperationException(e);
372         }
373     }
374 
375     public void addMojos(List<MojoDescriptor> mojos) throws DuplicateMojoDescriptorException {
376         for (MojoDescriptor mojoDescriptor : mojos) {
377             addMojo(mojoDescriptor);
378         }
379     }
380 }