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