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.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.Collections;
28  import java.util.List;
29  import java.util.Map;
30  
31  import org.apache.maven.artifact.repository.metadata.Metadata;
32  import org.apache.maven.artifact.repository.metadata.io.MetadataReader;
33  import org.apache.maven.model.Build;
34  import org.apache.maven.model.Plugin;
35  import org.apache.maven.plugin.BuildPluginManager;
36  import org.apache.maven.plugin.descriptor.PluginDescriptor;
37  import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
38  import org.apache.maven.plugin.prefix.PluginPrefixRequest;
39  import org.apache.maven.plugin.prefix.PluginPrefixResolver;
40  import org.apache.maven.plugin.prefix.PluginPrefixResult;
41  import org.eclipse.aether.DefaultRepositorySystemSession;
42  import org.eclipse.aether.RepositoryEvent;
43  import org.eclipse.aether.RepositoryEvent.EventType;
44  import org.eclipse.aether.RepositoryListener;
45  import org.eclipse.aether.RepositorySystem;
46  import org.eclipse.aether.RepositorySystemSession;
47  import org.eclipse.aether.RequestTrace;
48  import org.eclipse.aether.metadata.DefaultMetadata;
49  import org.eclipse.aether.repository.ArtifactRepository;
50  import org.eclipse.aether.repository.RemoteRepository;
51  import org.eclipse.aether.repository.RepositoryPolicy;
52  import org.eclipse.aether.resolution.MetadataRequest;
53  import org.eclipse.aether.resolution.MetadataResult;
54  import org.slf4j.Logger;
55  import org.slf4j.LoggerFactory;
56  
57  /**
58   * Resolves a plugin prefix.
59   *
60   * @since 3.0
61   * @author Benjamin Bentmann
62   */
63  @Named
64  @Singleton
65  public class DefaultPluginPrefixResolver implements PluginPrefixResolver {
66      private static final String REPOSITORY_CONTEXT = "plugin";
67  
68      private final Logger logger = LoggerFactory.getLogger(getClass());
69      private final BuildPluginManager pluginManager;
70      private final RepositorySystem repositorySystem;
71      private final MetadataReader metadataReader;
72  
73      @Inject
74      public DefaultPluginPrefixResolver(
75              BuildPluginManager pluginManager, RepositorySystem repositorySystem, MetadataReader metadataReader) {
76          this.pluginManager = pluginManager;
77          this.repositorySystem = repositorySystem;
78          this.metadataReader = metadataReader;
79      }
80  
81      public PluginPrefixResult resolve(PluginPrefixRequest request) throws NoPluginFoundForPrefixException {
82          logger.debug("Resolving plugin prefix " + request.getPrefix() + " from " + request.getPluginGroups());
83  
84          PluginPrefixResult result = resolveFromProject(request);
85  
86          if (result == null) {
87              result = resolveFromRepository(request);
88  
89              if (result == null) {
90                  throw new NoPluginFoundForPrefixException(
91                          request.getPrefix(),
92                          request.getPluginGroups(),
93                          request.getRepositorySession().getLocalRepository(),
94                          request.getRepositories());
95              } else if (logger.isDebugEnabled()) {
96                  logger.debug("Resolved plugin prefix " + request.getPrefix() + " to " + result.getGroupId() + ":"
97                          + result.getArtifactId() + " from repository "
98                          + (result.getRepository() != null
99                                  ? result.getRepository().getId()
100                                 : "null"));
101             }
102         } else if (logger.isDebugEnabled()) {
103             logger.debug("Resolved plugin prefix " + request.getPrefix() + " to " + result.getGroupId() + ":"
104                     + result.getArtifactId() + " from POM " + request.getPom());
105         }
106 
107         return result;
108     }
109 
110     private PluginPrefixResult resolveFromProject(PluginPrefixRequest request) {
111         PluginPrefixResult result = null;
112 
113         if (request.getPom() != null && request.getPom().getBuild() != null) {
114             Build build = request.getPom().getBuild();
115 
116             result = resolveFromProject(request, build.getPlugins());
117 
118             if (result == null && build.getPluginManagement() != null) {
119                 result = resolveFromProject(request, build.getPluginManagement().getPlugins());
120             }
121         }
122 
123         return result;
124     }
125 
126     private PluginPrefixResult resolveFromProject(PluginPrefixRequest request, List<Plugin> plugins) {
127         for (Plugin plugin : plugins) {
128             try {
129                 PluginDescriptor pluginDescriptor =
130                         pluginManager.loadPlugin(plugin, request.getRepositories(), request.getRepositorySession());
131 
132                 if (request.getPrefix().equals(pluginDescriptor.getGoalPrefix())) {
133                     return new DefaultPluginPrefixResult(plugin);
134                 }
135             } catch (Exception e) {
136                 if (logger.isDebugEnabled()) {
137                     logger.warn(
138                             "Failed to retrieve plugin descriptor for " + plugin.getId() + ": " + e.getMessage(), e);
139                 } else {
140                     logger.warn("Failed to retrieve plugin descriptor for " + plugin.getId() + ": " + e.getMessage());
141                 }
142             }
143         }
144 
145         return null;
146     }
147 
148     private PluginPrefixResult resolveFromRepository(PluginPrefixRequest request) {
149         RequestTrace trace = RequestTrace.newChild(null, request);
150 
151         List<MetadataRequest> requests = new ArrayList<>();
152 
153         for (String pluginGroup : request.getPluginGroups()) {
154             org.eclipse.aether.metadata.Metadata metadata =
155                     new DefaultMetadata(pluginGroup, "maven-metadata.xml", DefaultMetadata.Nature.RELEASE_OR_SNAPSHOT);
156 
157             requests.add(new MetadataRequest(metadata, null, REPOSITORY_CONTEXT).setTrace(trace));
158 
159             for (RemoteRepository repository : request.getRepositories()) {
160                 requests.add(new MetadataRequest(metadata, repository, REPOSITORY_CONTEXT).setTrace(trace));
161             }
162         }
163 
164         // initial try, use locally cached metadata
165 
166         List<MetadataResult> results = repositorySystem.resolveMetadata(request.getRepositorySession(), requests);
167         requests.clear();
168 
169         PluginPrefixResult result = processResults(request, trace, results, requests);
170 
171         if (result != null) {
172             return result;
173         }
174 
175         // second try, refetch all (possibly outdated) metadata that wasn't updated in the first attempt
176 
177         if (!request.getRepositorySession().isOffline() && !requests.isEmpty()) {
178             DefaultRepositorySystemSession session = new DefaultRepositorySystemSession(request.getRepositorySession());
179             session.setUpdatePolicy(RepositoryPolicy.UPDATE_POLICY_ALWAYS);
180 
181             results = repositorySystem.resolveMetadata(session, requests);
182 
183             return processResults(request, trace, results, null);
184         }
185 
186         return null;
187     }
188 
189     private PluginPrefixResult processResults(
190             PluginPrefixRequest request,
191             RequestTrace trace,
192             List<MetadataResult> results,
193             List<MetadataRequest> requests) {
194         for (MetadataResult res : results) {
195             org.eclipse.aether.metadata.Metadata metadata = res.getMetadata();
196 
197             if (metadata != null) {
198                 ArtifactRepository repository = res.getRequest().getRepository();
199                 if (repository == null) {
200                     repository = request.getRepositorySession().getLocalRepository();
201                 }
202 
203                 PluginPrefixResult result =
204                         resolveFromRepository(request, trace, metadata.getGroupId(), metadata, repository);
205 
206                 if (result != null) {
207                     return result;
208                 }
209             }
210 
211             if (requests != null && !res.isUpdated()) {
212                 requests.add(res.getRequest());
213             }
214         }
215 
216         return null;
217     }
218 
219     private PluginPrefixResult resolveFromRepository(
220             PluginPrefixRequest request,
221             RequestTrace trace,
222             String pluginGroup,
223             org.eclipse.aether.metadata.Metadata metadata,
224             ArtifactRepository repository) {
225         if (metadata != null && metadata.getFile() != null && metadata.getFile().isFile()) {
226             try {
227                 Map<String, ?> options = Collections.singletonMap(MetadataReader.IS_STRICT, Boolean.FALSE);
228 
229                 Metadata pluginGroupMetadata = metadataReader.read(metadata.getFile(), options);
230 
231                 List<org.apache.maven.artifact.repository.metadata.Plugin> plugins = pluginGroupMetadata.getPlugins();
232 
233                 if (plugins != null) {
234                     for (org.apache.maven.artifact.repository.metadata.Plugin plugin : plugins) {
235                         if (request.getPrefix().equals(plugin.getPrefix())) {
236                             return new DefaultPluginPrefixResult(pluginGroup, plugin.getArtifactId(), repository);
237                         }
238                     }
239                 }
240             } catch (IOException e) {
241                 invalidMetadata(request.getRepositorySession(), trace, metadata, repository, e);
242             }
243         }
244 
245         return null;
246     }
247 
248     private void invalidMetadata(
249             RepositorySystemSession session,
250             RequestTrace trace,
251             org.eclipse.aether.metadata.Metadata metadata,
252             ArtifactRepository repository,
253             Exception exception) {
254         RepositoryListener listener = session.getRepositoryListener();
255         if (listener != null) {
256             RepositoryEvent.Builder event = new RepositoryEvent.Builder(session, EventType.METADATA_INVALID);
257             event.setTrace(trace);
258             event.setMetadata(metadata);
259             event.setException(exception);
260             event.setRepository(repository);
261             listener.metadataInvalid(event.build());
262         }
263     }
264 }