View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.repository;
20  
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.Collections;
24  import java.util.List;
25  import java.util.Objects;
26  import java.util.regex.Matcher;
27  import java.util.regex.Pattern;
28  
29  import static java.util.Objects.requireNonNull;
30  
31  /**
32   * A repository on a remote server.
33   */
34  public final class RemoteRepository implements ArtifactRepository {
35  
36      private static final Pattern URL_PATTERN =
37              Pattern.compile("([^:/]+(:[^:/]{2,}+(?=://))?):(//([^@/]*@)?([^/:]+))?.*");
38  
39      private final String id;
40  
41      private final String type;
42  
43      private final String url;
44  
45      private final String host;
46  
47      private final String protocol;
48  
49      private final RepositoryPolicy releasePolicy;
50  
51      private final RepositoryPolicy snapshotPolicy;
52  
53      private final Proxy proxy;
54  
55      private final Authentication authentication;
56  
57      private final List<RemoteRepository> mirroredRepositories;
58  
59      private final boolean repositoryManager;
60  
61      private final boolean blocked;
62  
63      RemoteRepository(Builder builder) {
64          if (builder.prototype != null) {
65              id = (builder.delta & Builder.ID) != 0 ? builder.id : builder.prototype.id;
66              type = (builder.delta & Builder.TYPE) != 0 ? builder.type : builder.prototype.type;
67              url = (builder.delta & Builder.URL) != 0 ? builder.url : builder.prototype.url;
68              releasePolicy =
69                      (builder.delta & Builder.RELEASES) != 0 ? builder.releasePolicy : builder.prototype.releasePolicy;
70              snapshotPolicy = (builder.delta & Builder.SNAPSHOTS) != 0
71                      ? builder.snapshotPolicy
72                      : builder.prototype.snapshotPolicy;
73              proxy = (builder.delta & Builder.PROXY) != 0 ? builder.proxy : builder.prototype.proxy;
74              authentication =
75                      (builder.delta & Builder.AUTH) != 0 ? builder.authentication : builder.prototype.authentication;
76              repositoryManager = (builder.delta & Builder.REPOMAN) != 0
77                      ? builder.repositoryManager
78                      : builder.prototype.repositoryManager;
79              blocked = (builder.delta & Builder.BLOCKED) != 0 ? builder.blocked : builder.prototype.blocked;
80              mirroredRepositories = (builder.delta & Builder.MIRRORED) != 0
81                      ? copy(builder.mirroredRepositories)
82                      : builder.prototype.mirroredRepositories;
83          } else {
84              id = builder.id;
85              type = builder.type;
86              url = builder.url;
87              releasePolicy = builder.releasePolicy;
88              snapshotPolicy = builder.snapshotPolicy;
89              proxy = builder.proxy;
90              authentication = builder.authentication;
91              repositoryManager = builder.repositoryManager;
92              blocked = builder.blocked;
93              mirroredRepositories = copy(builder.mirroredRepositories);
94          }
95  
96          Matcher m = URL_PATTERN.matcher(url);
97          if (m.matches()) {
98              protocol = m.group(1);
99              String host = m.group(5);
100             this.host = (host != null) ? host : "";
101         } else {
102             protocol = "";
103             host = "";
104         }
105     }
106 
107     private static List<RemoteRepository> copy(List<RemoteRepository> repos) {
108         if (repos == null || repos.isEmpty()) {
109             return Collections.emptyList();
110         }
111         return Collections.unmodifiableList(Arrays.asList(repos.toArray(new RemoteRepository[0])));
112     }
113 
114     public String getId() {
115         return id;
116     }
117 
118     public String getContentType() {
119         return type;
120     }
121 
122     /**
123      * Gets the (base) URL of this repository.
124      *
125      * @return The (base) URL of this repository, never {@code null}.
126      */
127     public String getUrl() {
128         return url;
129     }
130 
131     /**
132      * Gets the protocol part from the repository's URL, for example {@code file} or {@code http}. As suggested by RFC
133      * 2396, section 3.1 "Scheme Component", the protocol name should be treated case-insensitively.
134      *
135      * @return The protocol or an empty string if none, never {@code null}.
136      */
137     public String getProtocol() {
138         return protocol;
139     }
140 
141     /**
142      * Gets the host part from the repository's URL.
143      *
144      * @return The host or an empty string if none, never {@code null}.
145      */
146     public String getHost() {
147         return host;
148     }
149 
150     /**
151      * Gets the policy to apply for snapshot/release artifacts.
152      *
153      * @param snapshot {@code true} to retrieve the snapshot policy, {@code false} to retrieve the release policy.
154      * @return The requested repository policy, never {@code null}.
155      */
156     public RepositoryPolicy getPolicy(boolean snapshot) {
157         return snapshot ? snapshotPolicy : releasePolicy;
158     }
159 
160     /**
161      * Gets the proxy that has been selected for this repository.
162      *
163      * @return The selected proxy or {@code null} if none.
164      */
165     public Proxy getProxy() {
166         return proxy;
167     }
168 
169     /**
170      * Gets the authentication that has been selected for this repository.
171      *
172      * @return The selected authentication or {@code null} if none.
173      */
174     public Authentication getAuthentication() {
175         return authentication;
176     }
177 
178     /**
179      * Gets the repositories that this repository serves as a mirror for.
180      *
181      * @return The (read-only) repositories being mirrored by this repository, never {@code null}.
182      */
183     public List<RemoteRepository> getMirroredRepositories() {
184         return mirroredRepositories;
185     }
186 
187     /**
188      * Indicates whether this repository refers to a repository manager or not.
189      *
190      * @return {@code true} if this repository is a repository manager, {@code false} otherwise.
191      */
192     public boolean isRepositoryManager() {
193         return repositoryManager;
194     }
195 
196     /**
197      * Indicates whether this repository is blocked from performing any download requests.
198      *
199      * @return {@code true} if this repository is blocked from performing any download requests,
200      *         {@code false} otherwise.
201      */
202     public boolean isBlocked() {
203         return blocked;
204     }
205 
206     @Override
207     public String toString() {
208         StringBuilder buffer = new StringBuilder(256);
209         buffer.append(getId());
210         buffer.append(" (").append(getUrl());
211         buffer.append(", ").append(getContentType());
212         boolean r = getPolicy(false).isEnabled(), s = getPolicy(true).isEnabled();
213         if (r && s) {
214             buffer.append(", releases+snapshots");
215         } else if (r) {
216             buffer.append(", releases");
217         } else if (s) {
218             buffer.append(", snapshots");
219         } else {
220             buffer.append(", disabled");
221         }
222         if (isRepositoryManager()) {
223             buffer.append(", managed");
224         }
225         if (isBlocked()) {
226             buffer.append(", blocked");
227         }
228         buffer.append(")");
229         return buffer.toString();
230     }
231 
232     @Override
233     public boolean equals(Object obj) {
234         if (this == obj) {
235             return true;
236         }
237         if (obj == null || !getClass().equals(obj.getClass())) {
238             return false;
239         }
240 
241         RemoteRepository that = (RemoteRepository) obj;
242 
243         return Objects.equals(url, that.url)
244                 && Objects.equals(type, that.type)
245                 && Objects.equals(id, that.id)
246                 && Objects.equals(releasePolicy, that.releasePolicy)
247                 && Objects.equals(snapshotPolicy, that.snapshotPolicy)
248                 && Objects.equals(proxy, that.proxy)
249                 && Objects.equals(authentication, that.authentication)
250                 && Objects.equals(mirroredRepositories, that.mirroredRepositories)
251                 && repositoryManager == that.repositoryManager;
252     }
253 
254     @Override
255     public int hashCode() {
256         int hash = 17;
257         hash = hash * 31 + hash(url);
258         hash = hash * 31 + hash(type);
259         hash = hash * 31 + hash(id);
260         hash = hash * 31 + hash(releasePolicy);
261         hash = hash * 31 + hash(snapshotPolicy);
262         hash = hash * 31 + hash(proxy);
263         hash = hash * 31 + hash(authentication);
264         hash = hash * 31 + hash(mirroredRepositories);
265         hash = hash * 31 + (repositoryManager ? 1 : 0);
266         return hash;
267     }
268 
269     private static int hash(Object obj) {
270         return obj != null ? obj.hashCode() : 0;
271     }
272 
273     /**
274      * A builder to create remote repositories.
275      */
276     public static final class Builder {
277 
278         private static final RepositoryPolicy DEFAULT_POLICY = new RepositoryPolicy();
279 
280         static final int ID = 0x0001,
281                 TYPE = 0x0002,
282                 URL = 0x0004,
283                 RELEASES = 0x0008,
284                 SNAPSHOTS = 0x0010,
285                 PROXY = 0x0020,
286                 AUTH = 0x0040,
287                 MIRRORED = 0x0080,
288                 REPOMAN = 0x0100,
289                 BLOCKED = 0x0200;
290 
291         int delta;
292 
293         RemoteRepository prototype;
294 
295         String id;
296 
297         String type;
298 
299         String url;
300 
301         RepositoryPolicy releasePolicy = DEFAULT_POLICY;
302 
303         RepositoryPolicy snapshotPolicy = DEFAULT_POLICY;
304 
305         Proxy proxy;
306 
307         Authentication authentication;
308 
309         List<RemoteRepository> mirroredRepositories;
310 
311         boolean repositoryManager;
312 
313         boolean blocked;
314 
315         /**
316          * Creates a new repository builder.
317          *
318          * @param id The identifier of the repository, may be {@code null}.
319          * @param type The type of the repository, may be {@code null}.
320          * @param url The (base) URL of the repository, may be {@code null}.
321          */
322         public Builder(String id, String type, String url) {
323             this.id = (id != null) ? id : "";
324             this.type = (type != null) ? type : "";
325             this.url = (url != null) ? url : "";
326         }
327 
328         /**
329          * Creates a new repository builder which uses the specified remote repository as a prototype for the new one.
330          * All properties which have not been set on the builder will be copied from the prototype when building the
331          * repository.
332          *
333          * @param prototype The remote repository to use as prototype, must not be {@code null}.
334          */
335         public Builder(RemoteRepository prototype) {
336             this.prototype = requireNonNull(prototype, "remote repository prototype cannot be null");
337         }
338 
339         /**
340          * Builds a new remote repository from the current values of this builder. The state of the builder itself
341          * remains unchanged.
342          *
343          * @return The remote repository, never {@code null}.
344          */
345         public RemoteRepository build() {
346             if (prototype != null && delta == 0) {
347                 return prototype;
348             }
349             return new RemoteRepository(this);
350         }
351 
352         private <T> void delta(int flag, T builder, T prototype) {
353             boolean equal = Objects.equals(builder, prototype);
354             if (equal) {
355                 delta &= ~flag;
356             } else {
357                 delta |= flag;
358             }
359         }
360 
361         /**
362          * Sets the identifier of the repository.
363          *
364          * @param id The identifier of the repository, may be {@code null}.
365          * @return This builder for chaining, never {@code null}.
366          */
367         public Builder setId(String id) {
368             this.id = (id != null) ? id : "";
369             if (prototype != null) {
370                 delta(ID, this.id, prototype.getId());
371             }
372             return this;
373         }
374 
375         /**
376          * Sets the type of the repository, e.g. "default".
377          *
378          * @param type The type of the repository, may be {@code null}.
379          * @return This builder for chaining, never {@code null}.
380          */
381         public Builder setContentType(String type) {
382             this.type = (type != null) ? type : "";
383             if (prototype != null) {
384                 delta(TYPE, this.type, prototype.getContentType());
385             }
386             return this;
387         }
388 
389         /**
390          * Sets the (base) URL of the repository.
391          *
392          * @param url The URL of the repository, may be {@code null}.
393          * @return This builder for chaining, never {@code null}.
394          */
395         public Builder setUrl(String url) {
396             this.url = (url != null) ? url : "";
397             if (prototype != null) {
398                 delta(URL, this.url, prototype.getUrl());
399             }
400             return this;
401         }
402 
403         /**
404          * Sets the policy to apply for snapshot and release artifacts.
405          *
406          * @param policy The repository policy to set, may be {@code null} to use a default policy.
407          * @return This builder for chaining, never {@code null}.
408          */
409         public Builder setPolicy(RepositoryPolicy policy) {
410             this.releasePolicy = (policy != null) ? policy : DEFAULT_POLICY;
411             this.snapshotPolicy = (policy != null) ? policy : DEFAULT_POLICY;
412             if (prototype != null) {
413                 delta(RELEASES, this.releasePolicy, prototype.getPolicy(false));
414                 delta(SNAPSHOTS, this.snapshotPolicy, prototype.getPolicy(true));
415             }
416             return this;
417         }
418 
419         /**
420          * Sets the policy to apply for release artifacts.
421          *
422          * @param releasePolicy The repository policy to set, may be {@code null} to use a default policy.
423          * @return This builder for chaining, never {@code null}.
424          */
425         public Builder setReleasePolicy(RepositoryPolicy releasePolicy) {
426             this.releasePolicy = (releasePolicy != null) ? releasePolicy : DEFAULT_POLICY;
427             if (prototype != null) {
428                 delta(RELEASES, this.releasePolicy, prototype.getPolicy(false));
429             }
430             return this;
431         }
432 
433         /**
434          * Sets the policy to apply for snapshot artifacts.
435          *
436          * @param snapshotPolicy The repository policy to set, may be {@code null} to use a default policy.
437          * @return This builder for chaining, never {@code null}.
438          */
439         public Builder setSnapshotPolicy(RepositoryPolicy snapshotPolicy) {
440             this.snapshotPolicy = (snapshotPolicy != null) ? snapshotPolicy : DEFAULT_POLICY;
441             if (prototype != null) {
442                 delta(SNAPSHOTS, this.snapshotPolicy, prototype.getPolicy(true));
443             }
444             return this;
445         }
446 
447         /**
448          * Sets the proxy to use in order to access the repository.
449          *
450          * @param proxy The proxy to use, may be {@code null}.
451          * @return This builder for chaining, never {@code null}.
452          */
453         public Builder setProxy(Proxy proxy) {
454             this.proxy = proxy;
455             if (prototype != null) {
456                 delta(PROXY, this.proxy, prototype.getProxy());
457             }
458             return this;
459         }
460 
461         /**
462          * Sets the authentication to use in order to access the repository.
463          *
464          * @param authentication The authentication to use, may be {@code null}.
465          * @return This builder for chaining, never {@code null}.
466          */
467         public Builder setAuthentication(Authentication authentication) {
468             this.authentication = authentication;
469             if (prototype != null) {
470                 delta(AUTH, this.authentication, prototype.getAuthentication());
471             }
472             return this;
473         }
474 
475         /**
476          * Sets the repositories being mirrored by the repository.
477          *
478          * @param mirroredRepositories The repositories being mirrored by the repository, may be {@code null}.
479          * @return This builder for chaining, never {@code null}.
480          */
481         public Builder setMirroredRepositories(List<RemoteRepository> mirroredRepositories) {
482             if (this.mirroredRepositories == null) {
483                 this.mirroredRepositories = new ArrayList<>();
484             } else {
485                 this.mirroredRepositories.clear();
486             }
487             if (mirroredRepositories != null) {
488                 this.mirroredRepositories.addAll(mirroredRepositories);
489             }
490             if (prototype != null) {
491                 delta(MIRRORED, this.mirroredRepositories, prototype.getMirroredRepositories());
492             }
493             return this;
494         }
495 
496         /**
497          * Adds the specified repository to the list of repositories being mirrored by the repository. If this builder
498          * was {@link Builder constructed from a prototype}, the given repository
499          * will be added to the list of mirrored repositories from the prototype.
500          *
501          * @param mirroredRepository The repository being mirrored by the repository, may be {@code null}.
502          * @return This builder for chaining, never {@code null}.
503          */
504         public Builder addMirroredRepository(RemoteRepository mirroredRepository) {
505             if (mirroredRepository != null) {
506                 if (this.mirroredRepositories == null) {
507                     this.mirroredRepositories = new ArrayList<>();
508                     if (prototype != null) {
509                         mirroredRepositories.addAll(prototype.getMirroredRepositories());
510                     }
511                 }
512                 mirroredRepositories.add(mirroredRepository);
513                 if (prototype != null) {
514                     delta |= MIRRORED;
515                 }
516             }
517             return this;
518         }
519 
520         /**
521          * Marks the repository as a repository manager or not.
522          *
523          * @param repositoryManager {@code true} if the repository points at a repository manager, {@code false} if the
524          *            repository is just serving static contents.
525          * @return This builder for chaining, never {@code null}.
526          */
527         public Builder setRepositoryManager(boolean repositoryManager) {
528             this.repositoryManager = repositoryManager;
529             if (prototype != null) {
530                 delta(REPOMAN, this.repositoryManager, prototype.isRepositoryManager());
531             }
532             return this;
533         }
534 
535         /**
536          * Marks the repository as blocked or not.
537          *
538          * @param blocked {@code true} if the repository should not be allowed to perform any requests.
539          * @return This builder for chaining, never {@code null}.
540          */
541         public Builder setBlocked(boolean blocked) {
542             this.blocked = blocked;
543             if (prototype != null) {
544                 delta(BLOCKED, this.blocked, prototype.isBlocked());
545             }
546             return this;
547         }
548     }
549 }