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