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.eclipse.aether.RepositoryEvent.EventType;
042    import org.eclipse.aether.DefaultRepositorySystemSession;
043    import org.eclipse.aether.RepositoryEvent;
044    import org.eclipse.aether.RepositoryListener;
045    import org.eclipse.aether.RepositorySystem;
046    import org.eclipse.aether.RepositorySystemSession;
047    import org.eclipse.aether.RequestTrace;
048    import org.eclipse.aether.metadata.DefaultMetadata;
049    import org.eclipse.aether.repository.ArtifactRepository;
050    import org.eclipse.aether.repository.RemoteRepository;
051    import org.eclipse.aether.repository.RepositoryPolicy;
052    import org.eclipse.aether.resolution.MetadataRequest;
053    import 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 )
062    public 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<MetadataRequest>();
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    }