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