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.apache.maven.artifact.repository;
20  
21  import java.io.File;
22  import java.nio.file.Path;
23  import java.util.Collections;
24  import java.util.List;
25  import java.util.Objects;
26  
27  import org.apache.maven.artifact.Artifact;
28  import org.apache.maven.artifact.metadata.ArtifactMetadata;
29  import org.apache.maven.artifact.repository.layout.ArtifactRepositoryLayout;
30  import org.apache.maven.repository.Proxy;
31  
32  /**
33   * Abstraction of an artifact repository. Artifact repositories can be remote, local, or even build reactor or
34   * IDE workspace.
35   */
36  // TODO completely separate local and remote artifact repositories
37  public class MavenArtifactRepository implements ArtifactRepository {
38      private static final String LS = System.lineSeparator();
39  
40      private String id;
41  
42      private String url;
43  
44      private String basedir;
45  
46      private Path basedirPath;
47  
48      private String protocol;
49  
50      private ArtifactRepositoryLayout layout;
51  
52      private ArtifactRepositoryPolicy snapshots;
53  
54      private ArtifactRepositoryPolicy releases;
55  
56      private Authentication authentication;
57  
58      private Proxy proxy;
59  
60      private List<ArtifactRepository> mirroredRepositories = Collections.emptyList();
61  
62      private boolean blocked;
63  
64      public MavenArtifactRepository() {}
65  
66      /**
67       * Create a remote download repository.
68       *
69       * @param id        the unique identifier of the repository
70       * @param url       the URL of the repository
71       * @param layout    the layout of the repository
72       * @param snapshots the policies to use for snapshots
73       * @param releases  the policies to use for releases
74       */
75      public MavenArtifactRepository(
76              String id,
77              String url,
78              ArtifactRepositoryLayout layout,
79              ArtifactRepositoryPolicy snapshots,
80              ArtifactRepositoryPolicy releases) {
81          this.id = id;
82          this.url = url;
83          this.layout = layout;
84          this.snapshots = snapshots;
85          this.releases = releases;
86          //
87          // Derive these from the URL
88          //
89          this.protocol = protocol(url);
90          this.basedir = basedir(url);
91      }
92  
93      public MavenArtifactRepository(
94              String id,
95              Path path,
96              ArtifactRepositoryLayout layout,
97              ArtifactRepositoryPolicy snapshots,
98              ArtifactRepositoryPolicy releases) {
99          this.id = id;
100         this.url = path.toUri().toString();
101         this.layout = layout;
102         this.snapshots = snapshots;
103         this.releases = releases;
104         //
105         // Derive these from the URL
106         //
107         this.protocol = path.toUri().toString();
108         this.basedir = path.toString();
109         this.basedirPath = path;
110     }
111 
112     @Override
113     public String pathOf(Artifact artifact) {
114         return layout.pathOf(artifact);
115     }
116 
117     @Override
118     public String pathOfRemoteRepositoryMetadata(ArtifactMetadata artifactMetadata) {
119         return layout.pathOfRemoteRepositoryMetadata(artifactMetadata);
120     }
121 
122     @Override
123     public String pathOfLocalRepositoryMetadata(ArtifactMetadata metadata, ArtifactRepository repository) {
124         return layout.pathOfLocalRepositoryMetadata(metadata, repository);
125     }
126 
127     @Override
128     public void setLayout(ArtifactRepositoryLayout layout) {
129         this.layout = layout;
130     }
131 
132     @Override
133     public ArtifactRepositoryLayout getLayout() {
134         return layout;
135     }
136 
137     @Override
138     public void setSnapshotUpdatePolicy(ArtifactRepositoryPolicy snapshots) {
139         this.snapshots = snapshots;
140     }
141 
142     @Override
143     public ArtifactRepositoryPolicy getSnapshots() {
144         return snapshots;
145     }
146 
147     @Override
148     public void setReleaseUpdatePolicy(ArtifactRepositoryPolicy releases) {
149         this.releases = releases;
150     }
151 
152     @Override
153     public ArtifactRepositoryPolicy getReleases() {
154         return releases;
155     }
156 
157     @Override
158     public String getKey() {
159         return getId();
160     }
161 
162     @Override
163     public String toString() {
164         StringBuilder sb = new StringBuilder(256);
165 
166         sb.append("      id: ").append(getId()).append(LS);
167         sb.append("      url: ").append(getUrl()).append(LS);
168         sb.append("   layout: ").append(layout != null ? layout : "none");
169 
170         if (proxy != null) {
171             sb.append(LS)
172                     .append("    proxy: ")
173                     .append(proxy.getHost())
174                     .append(':')
175                     .append(proxy.getPort());
176         }
177 
178         if (snapshots != null) {
179             sb.append(LS).append("snapshots: [enabled => ").append(snapshots.isEnabled());
180             sb.append(", update => ").append(snapshots.getUpdatePolicy()).append(']');
181         }
182 
183         if (releases != null) {
184             sb.append(LS).append("releases: [enabled => ").append(releases.isEnabled());
185             sb.append(", update => ").append(releases.getUpdatePolicy()).append(']');
186         }
187 
188         sb.append("   blocked: ").append(isBlocked()).append('\n');
189 
190         return sb.toString();
191     }
192 
193     @Override
194     public Artifact find(Artifact artifact) {
195         File artifactFile = new File(getBasedir(), pathOf(artifact));
196 
197         // We need to set the file here or the resolver will fail with an NPE, not fully equipped to deal
198         // with multiple local repository implementations yet.
199         artifact.setFile(artifactFile);
200 
201         return artifact;
202     }
203 
204     @Override
205     public List<String> findVersions(Artifact artifact) {
206         return Collections.emptyList();
207     }
208 
209     @Override
210     public String getId() {
211         return id;
212     }
213 
214     @Override
215     public String getUrl() {
216         return url;
217     }
218 
219     @Override
220     public String getBasedir() {
221         return basedir;
222     }
223 
224     @Override
225     public Path getBasedirPath() {
226         return basedirPath;
227     }
228 
229     @Override
230     public String getProtocol() {
231         return protocol;
232     }
233 
234     @Override
235     public void setId(String id) {
236         this.id = id;
237     }
238 
239     @Override
240     public void setUrl(String url) {
241         this.url = url;
242 
243         this.protocol = protocol(url);
244         this.basedir = basedir(url);
245     }
246 
247     // Path Utils
248 
249     /**
250      * Return the protocol name.
251      * <br>
252      * E.g: for input
253      * <code>http://www.codehaus.org</code> this method will return <code>http</code>
254      *
255      * @param url the url
256      * @return the host name
257      */
258     private static String protocol(final String url) {
259         final int pos = url.indexOf(':');
260 
261         if (pos == -1) {
262             return "";
263         }
264         return url.substring(0, pos).trim();
265     }
266 
267     /**
268      * Derive the path portion of the given URL.
269      *
270      * @param url the repository URL
271      * @return the basedir of the repository
272      * TODO need to URL decode for spaces?
273      */
274     private String basedir(String url) {
275         String retValue = null;
276 
277         if (protocol.equalsIgnoreCase("file")) {
278             retValue = url.substring(protocol.length() + 1);
279             retValue = decode(retValue);
280             // special case: if omitted // on protocol, keep path as is
281             if (retValue.startsWith("//")) {
282                 retValue = retValue.substring(2);
283 
284                 if (retValue.length() >= 2 && (retValue.charAt(1) == '|' || retValue.charAt(1) == ':')) {
285                     // special case: if there is a windows drive letter, then keep the original return value
286                     retValue = retValue.charAt(0) + ":" + retValue.substring(2);
287                 } else {
288                     // Now we expect the host
289                     int index = retValue.indexOf('/');
290                     if (index >= 0) {
291                         retValue = retValue.substring(index + 1);
292                     }
293 
294                     // special case: if there is a windows drive letter, then keep the original return value
295                     if (retValue.length() >= 2 && (retValue.charAt(1) == '|' || retValue.charAt(1) == ':')) {
296                         retValue = retValue.charAt(0) + ":" + retValue.substring(2);
297                     } else if (index >= 0) {
298                         // leading / was previously stripped
299                         retValue = "/" + retValue;
300                     }
301                 }
302             }
303 
304             // special case: if there is a windows drive letter using |, switch to :
305             if (retValue.length() >= 2 && retValue.charAt(1) == '|') {
306                 retValue = retValue.charAt(0) + ":" + retValue.substring(2);
307             }
308 
309             // normalize separators
310             retValue = new File(retValue).getPath();
311         }
312 
313         if (retValue == null) {
314             retValue = "/";
315         }
316         return retValue.trim();
317     }
318 
319     /**
320      * Decodes the specified (portion of a) URL. <strong>Note:</strong> This decoder assumes that ISO-8859-1 is used to
321      * convert URL-encoded bytes to characters.
322      *
323      * @param url The URL to decode, may be <code>null</code>.
324      * @return The decoded URL or <code>null</code> if the input was <code>null</code>.
325      */
326     private static String decode(String url) {
327         String decoded = url;
328         if (url != null) {
329             int pos = -1;
330             while ((pos = decoded.indexOf('%', pos + 1)) >= 0) {
331                 if (pos + 2 < decoded.length()) {
332                     String hexStr = decoded.substring(pos + 1, pos + 3);
333                     char ch = (char) Integer.parseInt(hexStr, 16);
334                     decoded = decoded.substring(0, pos) + ch + decoded.substring(pos + 3);
335                 }
336             }
337         }
338         return decoded;
339     }
340 
341     @Override
342     public int hashCode() {
343         final int prime = 31;
344         int result = 1;
345         result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
346         return result;
347     }
348 
349     @Override
350     public boolean equals(Object obj) {
351         if (this == obj) {
352             return true;
353         }
354         if (obj == null) {
355             return false;
356         }
357         if (getClass() != obj.getClass()) {
358             return false;
359         }
360 
361         ArtifactRepository other = (ArtifactRepository) obj;
362 
363         return eq(getId(), other.getId());
364     }
365 
366     protected static <T> boolean eq(T s1, T s2) {
367         return Objects.equals(s1, s2);
368     }
369 
370     @Override
371     public Authentication getAuthentication() {
372         return authentication;
373     }
374 
375     @Override
376     public void setAuthentication(Authentication authentication) {
377         this.authentication = authentication;
378     }
379 
380     @Override
381     public Proxy getProxy() {
382         return proxy;
383     }
384 
385     @Override
386     public void setProxy(Proxy proxy) {
387         this.proxy = proxy;
388     }
389 
390     @Override
391     public boolean isBlacklisted() {
392         return false;
393     }
394 
395     @Override
396     public void setBlacklisted(boolean blackListed) {
397         // no op
398     }
399 
400     @Override
401     public boolean isUniqueVersion() {
402         return true;
403     }
404 
405     @Override
406     public boolean isProjectAware() {
407         return false;
408     }
409 
410     @Override
411     public List<ArtifactRepository> getMirroredRepositories() {
412         return mirroredRepositories;
413     }
414 
415     @Override
416     public void setMirroredRepositories(List<ArtifactRepository> mirroredRepositories) {
417         if (mirroredRepositories != null) {
418             this.mirroredRepositories = Collections.unmodifiableList(mirroredRepositories);
419         } else {
420             this.mirroredRepositories = Collections.emptyList();
421         }
422     }
423 
424     @Override
425     public boolean isBlocked() {
426         return blocked;
427     }
428 
429     @Override
430     public void setBlocked(boolean blocked) {
431         this.blocked = blocked;
432     }
433 }