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