1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.plugin.prefix.internal;
20
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 import javax.inject.Singleton;
24
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.LinkedHashMap;
30 import java.util.LinkedHashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 import java.util.stream.Collectors;
35 import java.util.stream.Stream;
36
37 import org.apache.maven.artifact.repository.metadata.Metadata;
38 import org.apache.maven.artifact.repository.metadata.io.MetadataReader;
39 import org.apache.maven.model.Build;
40 import org.apache.maven.model.Model;
41 import org.apache.maven.model.Plugin;
42 import org.apache.maven.model.PluginManagement;
43 import org.apache.maven.plugin.BuildPluginManager;
44 import org.apache.maven.plugin.descriptor.PluginDescriptor;
45 import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
46 import org.apache.maven.plugin.prefix.PluginPrefixRequest;
47 import org.apache.maven.plugin.prefix.PluginPrefixResolver;
48 import org.apache.maven.plugin.prefix.PluginPrefixResult;
49 import org.eclipse.aether.DefaultRepositorySystemSession;
50 import org.eclipse.aether.RepositoryEvent;
51 import org.eclipse.aether.RepositoryEvent.EventType;
52 import org.eclipse.aether.RepositoryListener;
53 import org.eclipse.aether.RepositorySystem;
54 import org.eclipse.aether.RepositorySystemSession;
55 import org.eclipse.aether.RequestTrace;
56 import org.eclipse.aether.metadata.DefaultMetadata;
57 import org.eclipse.aether.repository.ArtifactRepository;
58 import org.eclipse.aether.repository.RemoteRepository;
59 import org.eclipse.aether.repository.RepositoryPolicy;
60 import org.eclipse.aether.resolution.MetadataRequest;
61 import org.eclipse.aether.resolution.MetadataResult;
62 import org.slf4j.Logger;
63 import org.slf4j.LoggerFactory;
64
65
66
67
68
69
70
71 @Singleton
72 @Named
73 public class DefaultPluginPrefixResolver implements PluginPrefixResolver {
74
75 private static final String REPOSITORY_CONTEXT = "plugin";
76
77 private final Logger logger = LoggerFactory.getLogger(getClass());
78
79 @Inject
80 private BuildPluginManager pluginManager;
81
82 @Inject
83 private RepositorySystem repositorySystem;
84
85 @Inject
86 private MetadataReader metadataReader;
87
88 public PluginPrefixResult resolve(PluginPrefixRequest request) throws NoPluginFoundForPrefixException {
89 logger.debug("Resolving plugin prefix " + request.getPrefix() + " from " + request.getPluginGroups());
90
91 Model pom = request.getPom();
92 Build build = pom != null ? pom.getBuild() : null;
93 PluginManagement management = build != null ? build.getPluginManagement() : null;
94
95
96
97
98
99
100
101
102 LinkedHashMap<String, Set<String>> candidates = Stream.of(build, management)
103 .flatMap(container -> container != null ? container.getPlugins().stream() : Stream.empty())
104 .filter(p -> !request.getPluginGroups().contains(p.getGroupId()))
105 .collect(Collectors.groupingBy(
106 Plugin::getGroupId,
107 LinkedHashMap::new,
108 Collectors.mapping(Plugin::getArtifactId, Collectors.toSet())));
109 request.getPluginGroups().forEach(g -> candidates.put(g, null));
110 PluginPrefixResult result = null;
111
112
113 if (build != null) {
114 result = resolveFromProject(
115 request,
116 build.getPlugins(),
117 management != null ? management.getPlugins() : Collections.emptyList());
118 }
119
120
121
122 if (result == null) {
123 result = resolveFromRepository(request, candidates);
124 }
125
126 if (result == null) {
127 throw new NoPluginFoundForPrefixException(
128 request.getPrefix(),
129 new ArrayList<>(candidates.keySet()),
130 request.getRepositorySession().getLocalRepository(),
131 request.getRepositories());
132 } else {
133 logger.debug("Resolved plugin prefix " + request.getPrefix() + " to " + result.getGroupId() + ":"
134 + result.getArtifactId() + " from repository "
135 + (result.getRepository() != null ? result.getRepository().getId() : "null"));
136 }
137
138 return result;
139 }
140
141 private PluginPrefixResult resolveFromProject(
142 PluginPrefixRequest request, List<Plugin> plugins, List<Plugin> pluginMgmt) {
143 if (plugins.isEmpty() && pluginMgmt.isEmpty()) {
144 return null;
145 }
146 PluginPrefixResult result = null;
147
148 Set<Plugin> candidates = new LinkedHashSet<>();
149 Stream.concat(plugins.stream(), pluginMgmt.stream())
150 .filter(p -> p.getArtifactId().contains(request.getPrefix()))
151 .forEach(candidates::add);
152 if (!candidates.isEmpty()) {
153 result = doResolveFromProject(request, candidates);
154 }
155
156 if (result == null) {
157 Set<Plugin> remainder = new LinkedHashSet<>(plugins);
158 remainder.removeAll(candidates);
159 result = doResolveFromProject(request, remainder);
160 }
161 return result;
162 }
163
164 private PluginPrefixResult doResolveFromProject(PluginPrefixRequest request, Collection<Plugin> plugins) {
165 for (Plugin plugin : plugins) {
166 try {
167 PluginDescriptor pluginDescriptor =
168 pluginManager.loadPlugin(plugin, request.getRepositories(), request.getRepositorySession());
169
170 if (request.getPrefix().equals(pluginDescriptor.getGoalPrefix())) {
171 return new DefaultPluginPrefixResult(plugin);
172 }
173 } catch (Exception e) {
174 if (logger.isDebugEnabled()) {
175 logger.warn(
176 "Failed to retrieve plugin descriptor for " + plugin.getId() + ": " + e.getMessage(), e);
177 } else {
178 logger.warn("Failed to retrieve plugin descriptor for " + plugin.getId() + ": " + e.getMessage());
179 }
180 }
181 }
182
183 return null;
184 }
185
186 private PluginPrefixResult resolveFromRepository(
187 PluginPrefixRequest request, LinkedHashMap<String, Set<String>> candidates) {
188 RequestTrace trace = RequestTrace.newChild(null, request);
189
190 List<MetadataRequest> requests = new ArrayList<>();
191
192 for (String pluginGroup : candidates.keySet()) {
193 org.eclipse.aether.metadata.Metadata metadata =
194 new DefaultMetadata(pluginGroup, "maven-metadata.xml", DefaultMetadata.Nature.RELEASE_OR_SNAPSHOT);
195
196 requests.add(new MetadataRequest(metadata, null, REPOSITORY_CONTEXT).setTrace(trace));
197
198 for (RemoteRepository repository : request.getRepositories()) {
199 requests.add(new MetadataRequest(metadata, repository, REPOSITORY_CONTEXT).setTrace(trace));
200 }
201 }
202
203
204
205 List<MetadataResult> results = repositorySystem.resolveMetadata(request.getRepositorySession(), requests);
206 requests.clear();
207
208 PluginPrefixResult result = processResults(request, trace, results, requests, candidates);
209
210 if (result != null) {
211 return result;
212 }
213
214
215
216 if (!request.getRepositorySession().isOffline() && !requests.isEmpty()) {
217 DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(request.getRepositorySession());
218 session.setUpdatePolicy(RepositoryPolicy.UPDATE_POLICY_ALWAYS);
219
220 results = repositorySystem.resolveMetadata(session, requests);
221
222 return processResults(request, trace, results, null, candidates);
223 }
224
225 return null;
226 }
227
228 private PluginPrefixResult processResults(
229 PluginPrefixRequest request,
230 RequestTrace trace,
231 List<MetadataResult> results,
232 List<MetadataRequest> requests,
233 LinkedHashMap<String, Set<String>> candidates) {
234 for (MetadataResult res : results) {
235 org.eclipse.aether.metadata.Metadata metadata = res.getMetadata();
236
237 if (metadata != null) {
238 ArtifactRepository repository = res.getRequest().getRepository();
239 if (repository == null) {
240 repository = request.getRepositorySession().getLocalRepository();
241 }
242
243 PluginPrefixResult result =
244 resolveFromRepository(request, trace, metadata.getGroupId(), metadata, repository, candidates);
245
246 if (result != null) {
247 return result;
248 }
249 }
250
251 if (requests != null && !res.isUpdated()) {
252 requests.add(res.getRequest());
253 }
254 }
255
256 return null;
257 }
258
259 private PluginPrefixResult resolveFromRepository(
260 PluginPrefixRequest request,
261 RequestTrace trace,
262 String pluginGroup,
263 org.eclipse.aether.metadata.Metadata metadata,
264 ArtifactRepository repository,
265 LinkedHashMap<String, Set<String>> candidates) {
266 if (metadata != null && metadata.getFile() != null && metadata.getFile().isFile()) {
267 try {
268 Map<String, ?> options = Collections.singletonMap(MetadataReader.IS_STRICT, Boolean.FALSE);
269
270 Metadata pluginGroupMetadata = metadataReader.read(metadata.getFile(), options);
271
272 List<org.apache.maven.artifact.repository.metadata.Plugin> plugins = pluginGroupMetadata.getPlugins();
273
274 if (plugins != null) {
275 for (org.apache.maven.artifact.repository.metadata.Plugin plugin : plugins) {
276 if (request.getPrefix().equals(plugin.getPrefix())
277 && (candidates.get(pluginGroup) == null
278 || candidates.get(pluginGroup).contains(plugin.getArtifactId()))) {
279 return new DefaultPluginPrefixResult(pluginGroup, plugin.getArtifactId(), repository);
280 }
281 }
282 }
283 } catch (IOException e) {
284 invalidMetadata(request.getRepositorySession(), trace, metadata, repository, e);
285 }
286 }
287
288 return null;
289 }
290
291 private void invalidMetadata(
292 RepositorySystemSession session,
293 RequestTrace trace,
294 org.eclipse.aether.metadata.Metadata metadata,
295 ArtifactRepository repository,
296 Exception exception) {
297 RepositoryListener listener = session.getRepositoryListener();
298 if (listener != null) {
299 RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INVALID);
300 event.setTrace(trace);
301 event.setMetadata(metadata);
302 event.setException(exception);
303 event.setRepository(repository);
304 listener.metadataInvalid(event.build());
305 }
306 }
307 }