001package org.eclipse.aether.internal.impl;
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.util.ArrayList;
023import java.util.Arrays;
024import java.util.List;
025import java.util.ListIterator;
026import static java.util.Objects.requireNonNull;
027
028import javax.inject.Inject;
029import javax.inject.Named;
030
031import org.apache.commons.lang3.StringUtils;
032import org.eclipse.aether.RepositoryCache;
033import org.eclipse.aether.RepositorySystemSession;
034import org.eclipse.aether.impl.RemoteRepositoryManager;
035import org.eclipse.aether.impl.UpdatePolicyAnalyzer;
036import org.eclipse.aether.repository.Authentication;
037import org.eclipse.aether.repository.AuthenticationSelector;
038import org.eclipse.aether.repository.MirrorSelector;
039import org.eclipse.aether.repository.Proxy;
040import org.eclipse.aether.repository.ProxySelector;
041import org.eclipse.aether.repository.RemoteRepository;
042import org.eclipse.aether.repository.RepositoryPolicy;
043import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider;
044import org.eclipse.aether.spi.locator.Service;
045import org.eclipse.aether.spi.locator.ServiceLocator;
046import org.slf4j.Logger;
047import org.slf4j.LoggerFactory;
048
049/**
050 */
051@Named
052public class DefaultRemoteRepositoryManager
053    implements RemoteRepositoryManager, Service
054{
055
056    private static final class LoggedMirror
057    {
058
059        private final Object[] keys;
060
061        LoggedMirror( RemoteRepository original, RemoteRepository mirror )
062        {
063            keys = new Object[] { mirror.getId(), mirror.getUrl(), original.getId(), original.getUrl() };
064        }
065
066        @Override
067        public boolean equals( Object obj )
068        {
069            if ( this == obj )
070            {
071                return true;
072            }
073            else if ( !( obj instanceof LoggedMirror ) )
074            {
075                return false;
076            }
077            LoggedMirror that = (LoggedMirror) obj;
078            return Arrays.equals( keys, that.keys );
079        }
080
081        @Override
082        public int hashCode()
083        {
084            return Arrays.hashCode( keys );
085        }
086
087    }
088
089    private static final Logger LOGGER = LoggerFactory.getLogger( DefaultRemoteRepositoryManager.class );
090
091    private UpdatePolicyAnalyzer updatePolicyAnalyzer;
092
093    private ChecksumPolicyProvider checksumPolicyProvider;
094
095    public DefaultRemoteRepositoryManager()
096    {
097        // enables default constructor
098    }
099
100    @Inject
101    DefaultRemoteRepositoryManager( UpdatePolicyAnalyzer updatePolicyAnalyzer,
102                                    ChecksumPolicyProvider checksumPolicyProvider )
103    {
104        setUpdatePolicyAnalyzer( updatePolicyAnalyzer );
105        setChecksumPolicyProvider( checksumPolicyProvider );
106    }
107
108    public void initService( ServiceLocator locator )
109    {
110        setUpdatePolicyAnalyzer( locator.getService( UpdatePolicyAnalyzer.class ) );
111        setChecksumPolicyProvider( locator.getService( ChecksumPolicyProvider.class ) );
112    }
113
114    public DefaultRemoteRepositoryManager setUpdatePolicyAnalyzer( UpdatePolicyAnalyzer updatePolicyAnalyzer )
115    {
116        this.updatePolicyAnalyzer = requireNonNull( updatePolicyAnalyzer, "update policy analyzer cannot be null" );
117        return this;
118    }
119
120    public DefaultRemoteRepositoryManager setChecksumPolicyProvider( ChecksumPolicyProvider checksumPolicyProvider )
121    {
122        this.checksumPolicyProvider = requireNonNull(
123                checksumPolicyProvider, "checksum policy provider cannot be null" );
124        return this;
125    }
126
127    public List<RemoteRepository> aggregateRepositories( RepositorySystemSession session,
128                                                         List<RemoteRepository> dominantRepositories,
129                                                         List<RemoteRepository> recessiveRepositories,
130                                                         boolean recessiveIsRaw )
131    {
132        if ( recessiveRepositories.isEmpty() )
133        {
134            return dominantRepositories;
135        }
136
137        MirrorSelector mirrorSelector = session.getMirrorSelector();
138        AuthenticationSelector authSelector = session.getAuthenticationSelector();
139        ProxySelector proxySelector = session.getProxySelector();
140
141        List<RemoteRepository> result = new ArrayList<>( dominantRepositories );
142
143        next: for ( RemoteRepository recessiveRepository : recessiveRepositories )
144        {
145            RemoteRepository repository = recessiveRepository;
146
147            if ( recessiveIsRaw )
148            {
149                RemoteRepository mirrorRepository = mirrorSelector.getMirror( recessiveRepository );
150
151                if ( mirrorRepository != null )
152                {
153                    logMirror( session, recessiveRepository, mirrorRepository );
154                    repository = mirrorRepository;
155                }
156            }
157
158            String key = getKey( repository );
159
160            for ( ListIterator<RemoteRepository> it = result.listIterator(); it.hasNext(); )
161            {
162                RemoteRepository dominantRepository = it.next();
163
164                if ( key.equals( getKey( dominantRepository ) ) )
165                {
166                    if ( !dominantRepository.getMirroredRepositories().isEmpty()
167                        && !repository.getMirroredRepositories().isEmpty() )
168                    {
169                        RemoteRepository mergedRepository = mergeMirrors( session, dominantRepository, repository );
170                        if ( mergedRepository != dominantRepository )
171                        {
172                            it.set( mergedRepository );
173                        }
174                    }
175
176                    continue next;
177                }
178            }
179
180            if ( recessiveIsRaw )
181            {
182                RemoteRepository.Builder builder = null;
183                Authentication auth = authSelector.getAuthentication( repository );
184                if ( auth != null )
185                {
186                    builder = new RemoteRepository.Builder( repository );
187                    builder.setAuthentication( auth );
188                }
189                Proxy proxy = proxySelector.getProxy( repository );
190                if ( proxy != null )
191                {
192                    if ( builder == null )
193                    {
194                        builder = new RemoteRepository.Builder( repository );
195                    }
196                    builder.setProxy( proxy );
197                }
198                if ( builder != null )
199                {
200                    repository = builder.build();
201                }
202            }
203
204            result.add( repository );
205        }
206
207        return result;
208    }
209
210    private void logMirror( RepositorySystemSession session, RemoteRepository original, RemoteRepository mirror )
211    {
212        if ( !LOGGER.isDebugEnabled() )
213        {
214            return;
215        }
216        RepositoryCache cache = session.getCache();
217        if ( cache != null )
218        {
219            Object key = new LoggedMirror( original, mirror );
220            if ( cache.get( session, key ) != null )
221            {
222                return;
223            }
224            cache.put( session, key, Boolean.TRUE );
225        }
226        LOGGER.debug( "Using mirror {} ({}) for {} ({}).",
227                mirror.getId(), mirror.getUrl(), original.getId(), original.getUrl() );
228    }
229
230    private String getKey( RemoteRepository repository )
231    {
232        return repository.getId();
233    }
234
235    private RemoteRepository mergeMirrors( RepositorySystemSession session, RemoteRepository dominant,
236                                           RemoteRepository recessive )
237    {
238        RemoteRepository.Builder merged = null;
239        RepositoryPolicy releases = null, snapshots = null;
240
241        next: for ( RemoteRepository rec : recessive.getMirroredRepositories() )
242        {
243            String recKey = getKey( rec );
244
245            for ( RemoteRepository dom : dominant.getMirroredRepositories() )
246            {
247                if ( recKey.equals( getKey( dom ) ) )
248                {
249                    continue next;
250                }
251            }
252
253            if ( merged == null )
254            {
255                merged = new RemoteRepository.Builder( dominant );
256                releases = dominant.getPolicy( false );
257                snapshots = dominant.getPolicy( true );
258            }
259
260            releases = merge( session, releases, rec.getPolicy( false ), false );
261            snapshots = merge( session, snapshots, rec.getPolicy( true ), false );
262
263            merged.addMirroredRepository( rec );
264        }
265
266        if ( merged == null )
267        {
268            return dominant;
269        }
270        return merged.setReleasePolicy( releases ).setSnapshotPolicy( snapshots ).build();
271    }
272
273    public RepositoryPolicy getPolicy( RepositorySystemSession session, RemoteRepository repository, boolean releases,
274                                       boolean snapshots )
275    {
276        RepositoryPolicy policy1 = releases ? repository.getPolicy( false ) : null;
277        RepositoryPolicy policy2 = snapshots ? repository.getPolicy( true ) : null;
278        return merge( session, policy1, policy2, true );
279    }
280
281    private RepositoryPolicy merge( RepositorySystemSession session, RepositoryPolicy policy1,
282                                    RepositoryPolicy policy2, boolean globalPolicy )
283    {
284        RepositoryPolicy policy;
285
286        if ( policy2 == null )
287        {
288            if ( globalPolicy )
289            {
290                policy = merge( policy1, session.getUpdatePolicy(), session.getChecksumPolicy() );
291            }
292            else
293            {
294                policy = policy1;
295            }
296        }
297        else if ( policy1 == null )
298        {
299            if ( globalPolicy )
300            {
301                policy = merge( policy2, session.getUpdatePolicy(), session.getChecksumPolicy() );
302            }
303            else
304            {
305                policy = policy2;
306            }
307        }
308        else if ( !policy2.isEnabled() )
309        {
310            if ( globalPolicy )
311            {
312                policy = merge( policy1, session.getUpdatePolicy(), session.getChecksumPolicy() );
313            }
314            else
315            {
316                policy = policy1;
317            }
318        }
319        else if ( !policy1.isEnabled() )
320        {
321            if ( globalPolicy )
322            {
323                policy = merge( policy2, session.getUpdatePolicy(), session.getChecksumPolicy() );
324            }
325            else
326            {
327                policy = policy2;
328            }
329        }
330        else
331        {
332            String checksums = session.getChecksumPolicy();
333            //noinspection StatementWithEmptyBody
334            if ( globalPolicy && !StringUtils.isEmpty( checksums ) )
335            {
336                // use global override
337            }
338            else
339            {
340                checksums =
341                    checksumPolicyProvider.getEffectiveChecksumPolicy( session, policy1.getChecksumPolicy(),
342                                                                       policy2.getChecksumPolicy() );
343            }
344
345            String updates = session.getUpdatePolicy();
346            //noinspection StatementWithEmptyBody
347            if ( globalPolicy && !StringUtils.isEmpty( updates ) )
348            {
349                // use global override
350            }
351            else
352            {
353                updates =
354                    updatePolicyAnalyzer.getEffectiveUpdatePolicy( session, policy1.getUpdatePolicy(),
355                                                                   policy2.getUpdatePolicy() );
356            }
357
358            policy = new RepositoryPolicy( true, updates, checksums );
359        }
360
361        return policy;
362    }
363
364    private RepositoryPolicy merge( RepositoryPolicy policy, String updates, String checksums )
365    {
366        if ( policy != null )
367        {
368            if ( StringUtils.isEmpty( updates ) )
369            {
370                updates = policy.getUpdatePolicy();
371            }
372            if ( StringUtils.isEmpty( checksums ) )
373            {
374                checksums = policy.getChecksumPolicy();
375            }
376            if ( !policy.getUpdatePolicy().equals( updates ) || !policy.getChecksumPolicy().equals( checksums ) )
377            {
378                policy = new RepositoryPolicy( policy.isEnabled(), updates, checksums );
379            }
380        }
381        return policy;
382    }
383
384}