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