001package org.apache.maven.repository;
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.net.MalformedURLException;
023import java.net.URL;
024import java.util.List;
025
026import org.apache.maven.RepositoryUtils;
027import org.apache.maven.artifact.repository.ArtifactRepository;
028import org.apache.maven.settings.Mirror;
029import org.codehaus.plexus.component.annotations.Component;
030import org.codehaus.plexus.util.StringUtils;
031
032@Component( role = MirrorSelector.class )
033public class DefaultMirrorSelector
034    implements MirrorSelector
035{
036
037    private static final String WILDCARD = "*";
038
039    private static final String EXTERNAL_WILDCARD = "external:*";
040
041    public Mirror getMirror( ArtifactRepository repository, List<Mirror> mirrors )
042    {
043        String repoId = repository.getId();
044
045        if ( repoId != null && mirrors != null )
046        {
047            for ( Mirror mirror : mirrors )
048            {
049                if ( repoId.equals( mirror.getMirrorOf() ) && matchesLayout( repository, mirror ) )
050                {
051                    return mirror;
052                }
053            }
054
055            for ( Mirror mirror : mirrors )
056            {
057                if ( matchPattern( repository, mirror.getMirrorOf() ) && matchesLayout( repository, mirror ) )
058                {
059                    return mirror;
060                }
061            }
062        }
063
064        return null;
065    }
066
067    /**
068     * This method checks if the pattern matches the originalRepository. Valid patterns: * =
069     * everything external:* = everything not on the localhost and not file based. repo,repo1 = repo
070     * or repo1 *,!repo1 = everything except repo1
071     *
072     * @param originalRepository to compare for a match.
073     * @param pattern used for match. Currently only '*' is supported.
074     * @return true if the repository is a match to this pattern.
075     */
076    static boolean matchPattern( ArtifactRepository originalRepository, String pattern )
077    {
078        boolean result = false;
079        String originalId = originalRepository.getId();
080
081        // simple checks first to short circuit processing below.
082        if ( WILDCARD.equals( pattern ) || pattern.equals( originalId ) )
083        {
084            result = true;
085        }
086        else
087        {
088            // process the list
089            String[] repos = pattern.split( "," );
090            for ( String repo : repos )
091            {
092                // see if this is a negative match
093                if ( repo.length() > 1 && repo.startsWith( "!" ) )
094                {
095                    if ( repo.substring( 1 ).equals( originalId ) )
096                    {
097                        // explicitly exclude. Set result and stop processing.
098                        result = false;
099                        break;
100                    }
101                }
102                // check for exact match
103                else if ( repo.equals( originalId ) )
104                {
105                    result = true;
106                    break;
107                }
108                // check for external:*
109                else if ( EXTERNAL_WILDCARD.equals( repo ) && isExternalRepo( originalRepository ) )
110                {
111                    result = true;
112                    // don't stop processing in case a future segment explicitly excludes this repo
113                }
114                else if ( WILDCARD.equals( repo ) )
115                {
116                    result = true;
117                    // don't stop processing in case a future segment explicitly excludes this repo
118                }
119            }
120        }
121        return result;
122    }
123
124    /**
125     * Checks the URL to see if this repository refers to an external repository
126     *
127     * @param originalRepository
128     * @return true if external.
129     */
130    static boolean isExternalRepo( ArtifactRepository originalRepository )
131    {
132        try
133        {
134            URL url = new URL( originalRepository.getUrl() );
135            return !( url.getHost().equals( "localhost" ) || url.getHost().equals( "127.0.0.1" )
136                            || url.getProtocol().equals( "file" ) );
137        }
138        catch ( MalformedURLException e )
139        {
140            // bad url just skip it here. It should have been validated already, but the wagon lookup will deal with it
141            return false;
142        }
143    }
144
145    static boolean matchesLayout( ArtifactRepository repository, Mirror mirror )
146    {
147        return matchesLayout( RepositoryUtils.getLayout( repository ), mirror.getMirrorOfLayouts() );
148    }
149
150    /**
151     * Checks whether the layouts configured for a mirror match with the layout of the repository.
152     *
153     * @param repoLayout The layout of the repository, may be {@code null}.
154     * @param mirrorLayout The layouts supported by the mirror, may be {@code null}.
155     * @return {@code true} if the layouts associated with the mirror match the layout of the original repository,
156     *         {@code false} otherwise.
157     */
158    static boolean matchesLayout( String repoLayout, String mirrorLayout )
159    {
160        boolean result = false;
161
162        // simple checks first to short circuit processing below.
163        if ( StringUtils.isEmpty( mirrorLayout ) || WILDCARD.equals( mirrorLayout ) )
164        {
165            result = true;
166        }
167        else if ( mirrorLayout.equals( repoLayout ) )
168        {
169            result = true;
170        }
171        else
172        {
173            // process the list
174            String[] layouts = mirrorLayout.split( "," );
175            for ( String layout : layouts )
176            {
177                // see if this is a negative match
178                if ( layout.length() > 1 && layout.startsWith( "!" ) )
179                {
180                    if ( layout.substring( 1 ).equals( repoLayout ) )
181                    {
182                        // explicitly exclude. Set result and stop processing.
183                        result = false;
184                        break;
185                    }
186                }
187                // check for exact match
188                else if ( layout.equals( repoLayout ) )
189                {
190                    result = true;
191                    break;
192                }
193                else if ( WILDCARD.equals( layout ) )
194                {
195                    result = true;
196                    // don't stop processing in case a future segment explicitly excludes this repo
197                }
198            }
199        }
200
201        return result;
202    }
203
204}