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.List;
027import java.util.ListIterator;
028import java.util.stream.Collectors;
029
030import org.eclipse.aether.Keys;
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 Logger LOGGER = LoggerFactory.getLogger(DefaultRemoteRepositoryManager.class);
057
058    private final UpdatePolicyAnalyzer updatePolicyAnalyzer;
059
060    private final ChecksumPolicyProvider checksumPolicyProvider;
061
062    private final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory;
063
064    @Inject
065    public DefaultRemoteRepositoryManager(
066            UpdatePolicyAnalyzer updatePolicyAnalyzer,
067            ChecksumPolicyProvider checksumPolicyProvider,
068            RepositoryKeyFunctionFactory repositoryKeyFunctionFactory) {
069        this.updatePolicyAnalyzer = requireNonNull(updatePolicyAnalyzer, "update policy analyzer cannot be null");
070        this.checksumPolicyProvider = requireNonNull(checksumPolicyProvider, "checksum policy provider cannot be null");
071        this.repositoryKeyFunctionFactory =
072                requireNonNull(repositoryKeyFunctionFactory, "repository key function factory cannot be null");
073    }
074
075    @Override
076    public List<RemoteRepository> aggregateRepositories(
077            RepositorySystemSession session,
078            List<RemoteRepository> dominantRepositories,
079            List<RemoteRepository> recessiveRepositories,
080            boolean recessiveIsRaw) {
081        requireNonNull(session, "session cannot be null");
082        requireNonNull(dominantRepositories, "dominantRepositories cannot be null");
083        requireNonNull(recessiveRepositories, "recessiveRepositories cannot be null");
084        if (recessiveRepositories.isEmpty()) {
085            return dominantRepositories;
086        }
087
088        RepositoryKeyFunction repositoryKeyFunction = repositoryKeyFunctionFactory.systemRepositoryKeyFunction(session);
089        MirrorSelector mirrorSelector = session.getMirrorSelector();
090        AuthenticationSelector authSelector = session.getAuthenticationSelector();
091        ProxySelector proxySelector = session.getProxySelector();
092
093        List<RemoteRepository> result = new ArrayList<>(dominantRepositories);
094
095        next:
096        for (RemoteRepository recessiveRepository : recessiveRepositories) {
097            RemoteRepository repository = recessiveRepository;
098
099            if (recessiveIsRaw) {
100                RemoteRepository mirrorRepository = mirrorSelector.getMirror(recessiveRepository);
101
102                if (mirrorRepository != null) {
103                    logMirror(session, recessiveRepository, mirrorRepository);
104                    repository = mirrorRepository;
105                }
106            }
107
108            String key = repositoryKeyFunction.apply(repository, null);
109
110            for (ListIterator<RemoteRepository> it = result.listIterator(); it.hasNext(); ) {
111                RemoteRepository dominantRepository = it.next();
112
113                if (key.equals(repositoryKeyFunction.apply(dominantRepository, null))) {
114                    if (!dominantRepository.getMirroredRepositories().isEmpty()
115                            && !repository.getMirroredRepositories().isEmpty()) {
116                        RemoteRepository mergedRepository =
117                                mergeMirrors(session, repositoryKeyFunction, dominantRepository, repository);
118                        if (mergedRepository != dominantRepository) {
119                            it.set(mergedRepository);
120                        }
121                    }
122
123                    continue next;
124                }
125            }
126
127            if (recessiveIsRaw) {
128                RemoteRepository.Builder builder = null;
129                Authentication auth = authSelector.getAuthentication(repository);
130                if (auth != null) {
131                    builder = new RemoteRepository.Builder(repository);
132                    builder.setAuthentication(auth);
133                }
134                Proxy proxy = proxySelector.getProxy(repository);
135                if (proxy != null) {
136                    if (builder == null) {
137                        builder = new RemoteRepository.Builder(repository);
138                    }
139                    builder.setProxy(proxy);
140                }
141                if (builder != null) {
142                    repository = builder.build();
143                }
144            }
145
146            result.add(repository);
147        }
148
149        return result.stream()
150                .map(r -> new RemoteRepository.Builder(r)
151                        .setIntent(RemoteRepository.Intent.RESOLUTION)
152                        .build())
153                .collect(Collectors.toList());
154    }
155
156    private void logMirror(RepositorySystemSession session, RemoteRepository original, RemoteRepository mirror) {
157        if (!LOGGER.isDebugEnabled()) {
158            return;
159        }
160        RepositoryCache cache = session.getCache();
161        if (cache != null) {
162            Object key = Keys.of(mirror.getId(), mirror.getUrl(), original.getId(), original.getUrl());
163            if (cache.get(session, key) != null) {
164                return;
165            }
166            cache.put(session, key, Boolean.TRUE);
167        }
168        LOGGER.debug(
169                "Using mirror {} ({}) for {} ({}).",
170                mirror.getId(),
171                mirror.getUrl(),
172                original.getId(),
173                original.getUrl());
174    }
175
176    private RemoteRepository mergeMirrors(
177            RepositorySystemSession session,
178            RepositoryKeyFunction repositoryKeyFunction,
179            RemoteRepository dominant,
180            RemoteRepository recessive) {
181        RemoteRepository.Builder merged = null;
182        RepositoryPolicy releases = null, snapshots = null;
183
184        next:
185        for (RemoteRepository rec : recessive.getMirroredRepositories()) {
186            String recKey = repositoryKeyFunction.apply(rec, null);
187
188            for (RemoteRepository dom : dominant.getMirroredRepositories()) {
189                if (recKey.equals(repositoryKeyFunction.apply(dom, null))) {
190                    continue next;
191                }
192            }
193
194            if (merged == null) {
195                merged = new RemoteRepository.Builder(dominant);
196                releases = dominant.getPolicy(false);
197                snapshots = dominant.getPolicy(true);
198            }
199
200            releases = merge(session, releases, rec.getPolicy(false), false);
201            snapshots = merge(session, snapshots, rec.getPolicy(true), false);
202
203            merged.addMirroredRepository(rec);
204        }
205
206        if (merged == null) {
207            return dominant;
208        }
209        return merged.setReleasePolicy(releases).setSnapshotPolicy(snapshots).build();
210    }
211
212    @Override
213    public RepositoryPolicy getPolicy(
214            RepositorySystemSession session, RemoteRepository repository, boolean releases, boolean snapshots) {
215        requireNonNull(session, "session cannot be null");
216        requireNonNull(repository, "repository cannot be null");
217        RepositoryPolicy policy1 = releases ? repository.getPolicy(false) : null;
218        RepositoryPolicy policy2 = snapshots ? repository.getPolicy(true) : null;
219        return merge(session, policy1, policy2, true);
220    }
221
222    private RepositoryPolicy merge(
223            RepositorySystemSession session, RepositoryPolicy policy1, RepositoryPolicy policy2, boolean globalPolicy) {
224        RepositoryPolicy policy;
225
226        if (policy2 == null) {
227            if (globalPolicy) {
228                policy = merge(
229                        policy1,
230                        session.getArtifactUpdatePolicy(),
231                        session.getMetadataUpdatePolicy(),
232                        session.getChecksumPolicy());
233            } else {
234                policy = policy1;
235            }
236        } else if (policy1 == null) {
237            if (globalPolicy) {
238                policy = merge(
239                        policy2,
240                        session.getArtifactUpdatePolicy(),
241                        session.getMetadataUpdatePolicy(),
242                        session.getChecksumPolicy());
243            } else {
244                policy = policy2;
245            }
246        } else if (!policy2.isEnabled()) {
247            if (globalPolicy) {
248                policy = merge(
249                        policy1,
250                        session.getArtifactUpdatePolicy(),
251                        session.getMetadataUpdatePolicy(),
252                        session.getChecksumPolicy());
253            } else {
254                policy = policy1;
255            }
256        } else if (!policy1.isEnabled()) {
257            if (globalPolicy) {
258                policy = merge(
259                        policy2,
260                        session.getArtifactUpdatePolicy(),
261                        session.getMetadataUpdatePolicy(),
262                        session.getChecksumPolicy());
263            } else {
264                policy = policy2;
265            }
266        } else {
267            String checksums = session.getChecksumPolicy();
268            //noinspection StatementWithEmptyBody
269            if (globalPolicy && checksums != null && !checksums.isEmpty()) {
270                // use global override
271            } else {
272                checksums = checksumPolicyProvider.getEffectiveChecksumPolicy(
273                        session, policy1.getChecksumPolicy(), policy2.getChecksumPolicy());
274            }
275
276            String artifactUpdates = session.getArtifactUpdatePolicy();
277            //noinspection StatementWithEmptyBody
278            if (globalPolicy && artifactUpdates != null && !artifactUpdates.isEmpty()) {
279                // use global override
280            } else {
281                artifactUpdates = updatePolicyAnalyzer.getEffectiveUpdatePolicy(
282                        session, policy1.getArtifactUpdatePolicy(), policy2.getArtifactUpdatePolicy());
283            }
284            String metadataUpdates = session.getMetadataUpdatePolicy();
285            if (globalPolicy && metadataUpdates != null && !metadataUpdates.isEmpty()) {
286                // use global override
287            } else {
288                metadataUpdates = updatePolicyAnalyzer.getEffectiveUpdatePolicy(
289                        session, policy1.getMetadataUpdatePolicy(), policy2.getMetadataUpdatePolicy());
290            }
291
292            policy = new RepositoryPolicy(true, artifactUpdates, metadataUpdates, checksums);
293        }
294
295        return policy;
296    }
297
298    private RepositoryPolicy merge(
299            RepositoryPolicy policy, String artifactUpdates, String metadataUpdates, String checksums) {
300        if (policy != null) {
301            if (artifactUpdates == null || artifactUpdates.isEmpty()) {
302                artifactUpdates = policy.getArtifactUpdatePolicy();
303            }
304            if (metadataUpdates == null || metadataUpdates.isEmpty()) {
305                metadataUpdates = policy.getMetadataUpdatePolicy();
306            }
307            if (checksums == null || checksums.isEmpty()) {
308                checksums = policy.getChecksumPolicy();
309            }
310            if (!policy.getArtifactUpdatePolicy().equals(artifactUpdates)
311                    || !policy.getMetadataUpdatePolicy().equals(metadataUpdates)
312                    || !policy.getChecksumPolicy().equals(checksums)) {
313                policy = new RepositoryPolicy(policy.isEnabled(), artifactUpdates, metadataUpdates, checksums);
314            }
315        }
316        return policy;
317    }
318}