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