001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.eclipse.aether.internal.impl;
020
021import javax.inject.Inject;
022import javax.inject.Named;
023import javax.inject.Singleton;
024
025import java.util.ArrayList;
026import java.util.Arrays;
027import java.util.List;
028import java.util.ListIterator;
029import java.util.stream.Collectors;
030
031import org.eclipse.aether.RepositoryCache;
032import org.eclipse.aether.RepositorySystemSession;
033import org.eclipse.aether.impl.RemoteRepositoryManager;
034import org.eclipse.aether.impl.UpdatePolicyAnalyzer;
035import org.eclipse.aether.repository.Authentication;
036import org.eclipse.aether.repository.AuthenticationSelector;
037import org.eclipse.aether.repository.MirrorSelector;
038import org.eclipse.aether.repository.Proxy;
039import org.eclipse.aether.repository.ProxySelector;
040import org.eclipse.aether.repository.RemoteRepository;
041import org.eclipse.aether.repository.RepositoryKeyFunction;
042import org.eclipse.aether.repository.RepositoryPolicy;
043import org.eclipse.aether.spi.connector.checksum.ChecksumPolicyProvider;
044import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory;
045import org.slf4j.Logger;
046import org.slf4j.LoggerFactory;
047
048import static java.util.Objects.requireNonNull;
049
050/**
051 */
052@Singleton
053@Named
054public class DefaultRemoteRepositoryManager implements RemoteRepositoryManager {
055
056    private static final class LoggedMirror {
057
058        private final Object[] keys;
059
060        LoggedMirror(RemoteRepository original, RemoteRepository mirror) {
061            keys = new Object[] {mirror.getId(), mirror.getUrl(), original.getId(), original.getUrl()};
062        }
063
064        @Override
065        public boolean equals(Object obj) {
066            if (this == obj) {
067                return true;
068            } else if (!(obj instanceof LoggedMirror)) {
069                return false;
070            }
071            LoggedMirror that = (LoggedMirror) obj;
072            return Arrays.equals(keys, that.keys);
073        }
074
075        @Override
076        public int hashCode() {
077            return Arrays.hashCode(keys);
078        }
079    }
080
081    private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRemoteRepositoryManager.class);
082
083    private final UpdatePolicyAnalyzer updatePolicyAnalyzer;
084
085    private final ChecksumPolicyProvider checksumPolicyProvider;
086
087    private final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory;
088
089    @Inject
090    public DefaultRemoteRepositoryManager(
091            UpdatePolicyAnalyzer updatePolicyAnalyzer,
092            ChecksumPolicyProvider checksumPolicyProvider,
093            RepositoryKeyFunctionFactory repositoryKeyFunctionFactory) {
094        this.updatePolicyAnalyzer = requireNonNull(updatePolicyAnalyzer, "update policy analyzer cannot be null");
095        this.checksumPolicyProvider = requireNonNull(checksumPolicyProvider, "checksum policy provider cannot be null");
096        this.repositoryKeyFunctionFactory =
097                requireNonNull(repositoryKeyFunctionFactory, "repository key function factory cannot be null");
098    }
099
100    @Override
101    public List<RemoteRepository> aggregateRepositories(
102            RepositorySystemSession session,
103            List<RemoteRepository> dominantRepositories,
104            List<RemoteRepository> recessiveRepositories,
105            boolean recessiveIsRaw) {
106        requireNonNull(session, "session cannot be null");
107        requireNonNull(dominantRepositories, "dominantRepositories cannot be null");
108        requireNonNull(recessiveRepositories, "recessiveRepositories cannot be null");
109        if (recessiveRepositories.isEmpty()) {
110            return dominantRepositories;
111        }
112
113        RepositoryKeyFunction repositoryKeyFunction = repositoryKeyFunctionFactory.systemRepositoryKeyFunction(session);
114        MirrorSelector mirrorSelector = session.getMirrorSelector();
115        AuthenticationSelector authSelector = session.getAuthenticationSelector();
116        ProxySelector proxySelector = session.getProxySelector();
117
118        List<RemoteRepository> result = new ArrayList<>(dominantRepositories);
119
120        next:
121        for (RemoteRepository recessiveRepository : recessiveRepositories) {
122            RemoteRepository repository = recessiveRepository;
123
124            if (recessiveIsRaw) {
125                RemoteRepository mirrorRepository = mirrorSelector.getMirror(recessiveRepository);
126
127                if (mirrorRepository != null) {
128                    logMirror(session, recessiveRepository, mirrorRepository);
129                    repository = mirrorRepository;
130                }
131            }
132
133            String key = repositoryKeyFunction.apply(repository, null);
134
135            for (ListIterator<RemoteRepository> it = result.listIterator(); it.hasNext(); ) {
136                RemoteRepository dominantRepository = it.next();
137
138                if (key.equals(repositoryKeyFunction.apply(dominantRepository, null))) {
139                    if (!dominantRepository.getMirroredRepositories().isEmpty()
140                            && !repository.getMirroredRepositories().isEmpty()) {
141                        RemoteRepository mergedRepository =
142                                mergeMirrors(session, repositoryKeyFunction, dominantRepository, repository);
143                        if (mergedRepository != dominantRepository) {
144                            it.set(mergedRepository);
145                        }
146                    }
147
148                    continue next;
149                }
150            }
151
152            if (recessiveIsRaw) {
153                RemoteRepository.Builder builder = null;
154                Authentication auth = authSelector.getAuthentication(repository);
155                if (auth != null) {
156                    builder = new RemoteRepository.Builder(repository);
157                    builder.setAuthentication(auth);
158                }
159                Proxy proxy = proxySelector.getProxy(repository);
160                if (proxy != null) {
161                    if (builder == null) {
162                        builder = new RemoteRepository.Builder(repository);
163                    }
164                    builder.setProxy(proxy);
165                }
166                if (builder != null) {
167                    repository = builder.build();
168                }
169            }
170
171            result.add(repository);
172        }
173
174        return result.stream()
175                .map(r -> new RemoteRepository.Builder(r)
176                        .setIntent(RemoteRepository.Intent.RESOLUTION)
177                        .build())
178                .collect(Collectors.toList());
179    }
180
181    private void logMirror(RepositorySystemSession session, RemoteRepository original, RemoteRepository mirror) {
182        if (!LOGGER.isDebugEnabled()) {
183            return;
184        }
185        RepositoryCache cache = session.getCache();
186        if (cache != null) {
187            Object key = new LoggedMirror(original, mirror);
188            if (cache.get(session, key) != null) {
189                return;
190            }
191            cache.put(session, key, Boolean.TRUE);
192        }
193        LOGGER.debug(
194                "Using mirror {} ({}) for {} ({}).",
195                mirror.getId(),
196                mirror.getUrl(),
197                original.getId(),
198                original.getUrl());
199    }
200
201    private RemoteRepository mergeMirrors(
202            RepositorySystemSession session,
203            RepositoryKeyFunction repositoryKeyFunction,
204            RemoteRepository dominant,
205            RemoteRepository recessive) {
206        RemoteRepository.Builder merged = null;
207        RepositoryPolicy releases = null, snapshots = null;
208
209        next:
210        for (RemoteRepository rec : recessive.getMirroredRepositories()) {
211            String recKey = repositoryKeyFunction.apply(rec, null);
212
213            for (RemoteRepository dom : dominant.getMirroredRepositories()) {
214                if (recKey.equals(repositoryKeyFunction.apply(dom, null))) {
215                    continue next;
216                }
217            }
218
219            if (merged == null) {
220                merged = new RemoteRepository.Builder(dominant);
221                releases = dominant.getPolicy(false);
222                snapshots = dominant.getPolicy(true);
223            }
224
225            releases = merge(session, releases, rec.getPolicy(false), false);
226            snapshots = merge(session, snapshots, rec.getPolicy(true), false);
227
228            merged.addMirroredRepository(rec);
229        }
230
231        if (merged == null) {
232            return dominant;
233        }
234        return merged.setReleasePolicy(releases).setSnapshotPolicy(snapshots).build();
235    }
236
237    @Override
238    public RepositoryPolicy getPolicy(
239            RepositorySystemSession session, RemoteRepository repository, boolean releases, boolean snapshots) {
240        requireNonNull(session, "session cannot be null");
241        requireNonNull(repository, "repository cannot be null");
242        RepositoryPolicy policy1 = releases ? repository.getPolicy(false) : null;
243        RepositoryPolicy policy2 = snapshots ? repository.getPolicy(true) : null;
244        return merge(session, policy1, policy2, true);
245    }
246
247    private RepositoryPolicy merge(
248            RepositorySystemSession session, RepositoryPolicy policy1, RepositoryPolicy policy2, boolean globalPolicy) {
249        RepositoryPolicy policy;
250
251        if (policy2 == null) {
252            if (globalPolicy) {
253                policy = merge(
254                        policy1,
255                        session.getArtifactUpdatePolicy(),
256                        session.getMetadataUpdatePolicy(),
257                        session.getChecksumPolicy());
258            } else {
259                policy = policy1;
260            }
261        } else if (policy1 == null) {
262            if (globalPolicy) {
263                policy = merge(
264                        policy2,
265                        session.getArtifactUpdatePolicy(),
266                        session.getMetadataUpdatePolicy(),
267                        session.getChecksumPolicy());
268            } else {
269                policy = policy2;
270            }
271        } else if (!policy2.isEnabled()) {
272            if (globalPolicy) {
273                policy = merge(
274                        policy1,
275                        session.getArtifactUpdatePolicy(),
276                        session.getMetadataUpdatePolicy(),
277                        session.getChecksumPolicy());
278            } else {
279                policy = policy1;
280            }
281        } else if (!policy1.isEnabled()) {
282            if (globalPolicy) {
283                policy = merge(
284                        policy2,
285                        session.getArtifactUpdatePolicy(),
286                        session.getMetadataUpdatePolicy(),
287                        session.getChecksumPolicy());
288            } else {
289                policy = policy2;
290            }
291        } else {
292            String checksums = session.getChecksumPolicy();
293            //noinspection StatementWithEmptyBody
294            if (globalPolicy && checksums != null && !checksums.isEmpty()) {
295                // use global override
296            } else {
297                checksums = checksumPolicyProvider.getEffectiveChecksumPolicy(
298                        session, policy1.getChecksumPolicy(), policy2.getChecksumPolicy());
299            }
300
301            String artifactUpdates = session.getArtifactUpdatePolicy();
302            //noinspection StatementWithEmptyBody
303            if (globalPolicy && artifactUpdates != null && !artifactUpdates.isEmpty()) {
304                // use global override
305            } else {
306                artifactUpdates = updatePolicyAnalyzer.getEffectiveUpdatePolicy(
307                        session, policy1.getArtifactUpdatePolicy(), policy2.getArtifactUpdatePolicy());
308            }
309            String metadataUpdates = session.getMetadataUpdatePolicy();
310            if (globalPolicy && metadataUpdates != null && !metadataUpdates.isEmpty()) {
311                // use global override
312            } else {
313                metadataUpdates = updatePolicyAnalyzer.getEffectiveUpdatePolicy(
314                        session, policy1.getMetadataUpdatePolicy(), policy2.getMetadataUpdatePolicy());
315            }
316
317            policy = new RepositoryPolicy(true, artifactUpdates, metadataUpdates, checksums);
318        }
319
320        return policy;
321    }
322
323    private RepositoryPolicy merge(
324            RepositoryPolicy policy, String artifactUpdates, String metadataUpdates, String checksums) {
325        if (policy != null) {
326            if (artifactUpdates == null || artifactUpdates.isEmpty()) {
327                artifactUpdates = policy.getArtifactUpdatePolicy();
328            }
329            if (metadataUpdates == null || metadataUpdates.isEmpty()) {
330                metadataUpdates = policy.getMetadataUpdatePolicy();
331            }
332            if (checksums == null || checksums.isEmpty()) {
333                checksums = policy.getChecksumPolicy();
334            }
335            if (!policy.getArtifactUpdatePolicy().equals(artifactUpdates)
336                    || !policy.getMetadataUpdatePolicy().equals(metadataUpdates)
337                    || !policy.getChecksumPolicy().equals(checksums)) {
338                policy = new RepositoryPolicy(policy.isEnabled(), artifactUpdates, metadataUpdates, checksums);
339            }
340        }
341        return policy;
342    }
343}