001package org.apache.maven.plugin.prefix.internal;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import java.io.IOException;
023import java.util.ArrayList;
024import java.util.Collections;
025import java.util.List;
026import java.util.Map;
027
028import org.apache.maven.artifact.repository.metadata.Metadata;
029import org.apache.maven.artifact.repository.metadata.io.MetadataReader;
030import org.apache.maven.model.Build;
031import org.apache.maven.model.Plugin;
032import org.apache.maven.plugin.BuildPluginManager;
033import org.apache.maven.plugin.descriptor.PluginDescriptor;
034import org.apache.maven.plugin.prefix.NoPluginFoundForPrefixException;
035import org.apache.maven.plugin.prefix.PluginPrefixRequest;
036import org.apache.maven.plugin.prefix.PluginPrefixResolver;
037import org.apache.maven.plugin.prefix.PluginPrefixResult;
038import org.codehaus.plexus.component.annotations.Component;
039import org.codehaus.plexus.component.annotations.Requirement;
040import org.codehaus.plexus.logging.Logger;
041import org.eclipse.aether.RepositoryEvent.EventType;
042import org.eclipse.aether.DefaultRepositorySystemSession;
043import org.eclipse.aether.RepositoryEvent;
044import org.eclipse.aether.RepositoryListener;
045import org.eclipse.aether.RepositorySystem;
046import org.eclipse.aether.RepositorySystemSession;
047import org.eclipse.aether.RequestTrace;
048import org.eclipse.aether.metadata.DefaultMetadata;
049import org.eclipse.aether.repository.ArtifactRepository;
050import org.eclipse.aether.repository.RemoteRepository;
051import org.eclipse.aether.repository.RepositoryPolicy;
052import org.eclipse.aether.resolution.MetadataRequest;
053import org.eclipse.aether.resolution.MetadataResult;
054
055/**
056 * Resolves a plugin prefix.
057 *
058 * @since 3.0
059 * @author Benjamin Bentmann
060 */
061@Component( role = PluginPrefixResolver.class )
062public class DefaultPluginPrefixResolver
063    implements PluginPrefixResolver
064{
065
066    private static final String REPOSITORY_CONTEXT = "plugin";
067
068    @Requirement
069    private Logger logger;
070
071    @Requirement
072    private BuildPluginManager pluginManager;
073
074    @Requirement
075    private RepositorySystem repositorySystem;
076
077    @Requirement
078    private MetadataReader metadataReader;
079
080    public PluginPrefixResult resolve( PluginPrefixRequest request )
081        throws NoPluginFoundForPrefixException
082    {
083        logger.debug( "Resolving plugin prefix " + request.getPrefix() + " from " + request.getPluginGroups() );
084
085        PluginPrefixResult result = resolveFromProject( request );
086
087        if ( result == null )
088        {
089            result = resolveFromRepository( request );
090
091            if ( result == null )
092            {
093                throw new NoPluginFoundForPrefixException( request.getPrefix(), request.getPluginGroups(),
094                                                           request.getRepositorySession().getLocalRepository(),
095                                                           request.getRepositories() );
096            }
097            else if ( logger.isDebugEnabled() )
098            {
099                logger.debug( "Resolved plugin prefix " + request.getPrefix() + " to " + result.getGroupId() + ":"
100                    + result.getArtifactId() + " from repository "
101                    + ( result.getRepository() != null ? result.getRepository().getId() : "null" ) );
102            }
103        }
104        else if ( logger.isDebugEnabled() )
105        {
106            logger.debug( "Resolved plugin prefix " + request.getPrefix() + " to " + result.getGroupId() + ":"
107                + result.getArtifactId() + " from POM " + request.getPom() );
108        }
109
110        return result;
111    }
112
113    private PluginPrefixResult resolveFromProject( PluginPrefixRequest request )
114    {
115        PluginPrefixResult result = null;
116
117        if ( request.getPom() != null && request.getPom().getBuild() != null )
118        {
119            Build build = request.getPom().getBuild();
120
121            result = resolveFromProject( request, build.getPlugins() );
122
123            if ( result == null && build.getPluginManagement() != null )
124            {
125                result = resolveFromProject( request, build.getPluginManagement().getPlugins() );
126            }
127        }
128
129        return result;
130    }
131
132    private PluginPrefixResult resolveFromProject( PluginPrefixRequest request, List<Plugin> plugins )
133    {
134        for ( Plugin plugin : plugins )
135        {
136            try
137            {
138                PluginDescriptor pluginDescriptor =
139                    pluginManager.loadPlugin( plugin, request.getRepositories(), request.getRepositorySession() );
140
141                if ( request.getPrefix().equals( pluginDescriptor.getGoalPrefix() ) )
142                {
143                    return new DefaultPluginPrefixResult( plugin );
144                }
145            }
146            catch ( Exception e )
147            {
148                if ( logger.isDebugEnabled() )
149                {
150                    logger.warn( "Failed to retrieve plugin descriptor for " + plugin.getId() + ": " + e.getMessage(),
151                                 e );
152                }
153                else
154                {
155                    logger.warn( "Failed to retrieve plugin descriptor for " + plugin.getId() + ": " + e.getMessage() );
156                }
157            }
158        }
159
160        return null;
161    }
162
163    private PluginPrefixResult resolveFromRepository( PluginPrefixRequest request )
164    {
165        RequestTrace trace = RequestTrace.newChild( null, request );
166
167        List<MetadataRequest> requests = new ArrayList<>();
168
169        for ( String pluginGroup : request.getPluginGroups() )
170        {
171            org.eclipse.aether.metadata.Metadata metadata =
172                new DefaultMetadata( pluginGroup, "maven-metadata.xml", DefaultMetadata.Nature.RELEASE_OR_SNAPSHOT );
173
174            requests.add( new MetadataRequest( metadata, null, REPOSITORY_CONTEXT ).setTrace( trace ) );
175
176            for ( RemoteRepository repository : request.getRepositories() )
177            {
178                requests.add( new MetadataRequest( metadata, repository, REPOSITORY_CONTEXT ).setTrace( trace ) );
179            }
180        }
181
182        // initial try, use locally cached metadata
183
184        List<MetadataResult> results = repositorySystem.resolveMetadata( request.getRepositorySession(), requests );
185        requests.clear();
186
187        PluginPrefixResult result = processResults( request, trace, results, requests );
188
189        if ( result != null )
190        {
191            return result;
192        }
193
194        // second try, refetch all (possibly outdated) metadata that wasn't updated in the first attempt
195
196        if ( !request.getRepositorySession().isOffline() && !requests.isEmpty() )
197        {
198            DefaultRepositorySystemSession session =
199                new DefaultRepositorySystemSession( request.getRepositorySession() );
200            session.setUpdatePolicy( RepositoryPolicy.UPDATE_POLICY_ALWAYS );
201
202            results = repositorySystem.resolveMetadata( session, requests );
203
204            return processResults( request, trace, results, null );
205        }
206
207        return null;
208    }
209
210    private PluginPrefixResult processResults( PluginPrefixRequest request, RequestTrace trace,
211                                               List<MetadataResult> results, List<MetadataRequest> requests )
212    {
213        for ( MetadataResult res : results )
214        {
215            org.eclipse.aether.metadata.Metadata metadata = res.getMetadata();
216
217            if ( metadata != null )
218            {
219                ArtifactRepository repository = res.getRequest().getRepository();
220                if ( repository == null )
221                {
222                    repository = request.getRepositorySession().getLocalRepository();
223                }
224
225                PluginPrefixResult result =
226                    resolveFromRepository( request, trace, metadata.getGroupId(), metadata, repository );
227
228                if ( result != null )
229                {
230                    return result;
231                }
232            }
233
234            if ( requests != null && !res.isUpdated() )
235            {
236                requests.add( res.getRequest() );
237            }
238        }
239
240        return null;
241    }
242
243    private PluginPrefixResult resolveFromRepository( PluginPrefixRequest request, RequestTrace trace,
244                                                      String pluginGroup,
245                                                      org.eclipse.aether.metadata.Metadata metadata,
246                                                      ArtifactRepository repository )
247    {
248        if ( metadata != null && metadata.getFile() != null && metadata.getFile().isFile() )
249        {
250            try
251            {
252                Map<String, ?> options = Collections.singletonMap( MetadataReader.IS_STRICT, Boolean.FALSE );
253
254                Metadata pluginGroupMetadata = metadataReader.read( metadata.getFile(), options );
255
256                List<org.apache.maven.artifact.repository.metadata.Plugin> plugins = pluginGroupMetadata.getPlugins();
257
258                if ( plugins != null )
259                {
260                    for ( org.apache.maven.artifact.repository.metadata.Plugin plugin : plugins )
261                    {
262                        if ( request.getPrefix().equals( plugin.getPrefix() ) )
263                        {
264                            return new DefaultPluginPrefixResult( pluginGroup, plugin.getArtifactId(), repository );
265                        }
266                    }
267                }
268            }
269            catch ( IOException e )
270            {
271                invalidMetadata( request.getRepositorySession(), trace, metadata, repository, e );
272            }
273        }
274
275        return null;
276    }
277
278    private void invalidMetadata( RepositorySystemSession session, RequestTrace trace,
279                                  org.eclipse.aether.metadata.Metadata metadata, ArtifactRepository repository,
280                                  Exception exception )
281    {
282        RepositoryListener listener = session.getRepositoryListener();
283        if ( listener != null )
284        {
285            RepositoryEvent.Builder event = new RepositoryEvent.Builder( session, EventType.METADATA_INVALID );
286            event.setTrace( trace );
287            event.setMetadata( metadata );
288            event.setException( exception );
289            event.setRepository( repository );
290            listener.metadataInvalid( event.build() );
291        }
292    }
293
294}