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.internal.impl;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.net.URI;
26  import java.net.URISyntaxException;
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.LinkedHashMap;
30  import java.util.List;
31  
32  import org.eclipse.aether.ConfigurationProperties;
33  import org.eclipse.aether.RepositorySystemSession;
34  import org.eclipse.aether.artifact.Artifact;
35  import org.eclipse.aether.metadata.Metadata;
36  import org.eclipse.aether.repository.RemoteRepository;
37  import org.eclipse.aether.spi.artifact.ArtifactPredicate;
38  import org.eclipse.aether.spi.artifact.ArtifactPredicateFactory;
39  import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
40  import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
41  import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
42  import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
43  import org.eclipse.aether.transfer.NoRepositoryLayoutException;
44  import org.eclipse.aether.util.ConfigUtils;
45  
46  import static java.util.Objects.requireNonNull;
47  
48  /**
49   * Provides a Maven-2 repository layout for repositories with content type {@code "default"}.
50   */
51  @Singleton
52  @Named(Maven2RepositoryLayoutFactory.NAME)
53  public final class Maven2RepositoryLayoutFactory implements RepositoryLayoutFactory {
54      public static final String NAME = "maven2";
55  
56      /**
57       * Comma-separated list of checksum algorithms with which checksums are validated (downloaded) and generated
58       * (uploaded) with this layout. Resolver by default supports following algorithms: MD5, SHA-1, SHA-256 and
59       * SHA-512. New algorithms can be added by implementing ChecksumAlgorithmFactory component. To configure separately
60       * checksums for download or upload, use {@link #CONFIG_PROP_DOWNLOAD_CHECKSUMS_ALGORITHMS} and
61       * {@link #CONFIG_PROP_UPLOAD_CHECKSUMS_ALGORITHMS} respectively.
62       *
63       * @since 1.8.0
64       * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
65       * @configurationType {@link java.lang.String}
66       * @configurationDefaultValue {@link #DEFAULT_CHECKSUMS_ALGORITHMS}
67       * @configurationRepoIdSuffix Yes
68       */
69      public static final String CONFIG_PROP_CHECKSUMS_ALGORITHMS =
70              ConfigurationProperties.PREFIX_CHECKSUMS + "checksumAlgorithms";
71  
72      public static final String DEFAULT_CHECKSUMS_ALGORITHMS = "SHA-1,MD5";
73  
74      /**
75       * Comma-separated list of checksum algorithms with which checksums are generated and uploaded
76       * with this layout. Resolver by default supports following algorithms: MD5, SHA-1, SHA-256 and
77       * SHA-512. New algorithms can be added by implementing ChecksumAlgorithmFactory component.
78       * If this property is set, it <em>overrides</em> the value set in {@link #CONFIG_PROP_CHECKSUMS_ALGORITHMS} for
79       * uploads.
80       *
81       * @since 2.0.15
82       * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
83       * @configurationType {@link java.lang.String}
84       * @configurationDefaultValue {@link #DEFAULT_CHECKSUMS_ALGORITHMS}
85       * @configurationRepoIdSuffix Yes
86       */
87      public static final String CONFIG_PROP_UPLOAD_CHECKSUMS_ALGORITHMS =
88              ConfigurationProperties.PREFIX_CHECKSUMS + "uploadChecksumAlgorithms";
89  
90      /**
91       * Comma-separated list of checksum algorithms with which checksums are validated (downloaded) with this layout.
92       * Resolver by default supports following algorithms: MD5, SHA-1, SHA-256 and SHA-512.
93       * New algorithms can be added by implementing ChecksumAlgorithmFactory component.
94       * If this property is set, it <em>overrides</em> the value set in {@link #CONFIG_PROP_CHECKSUMS_ALGORITHMS} for
95       * downloads.
96       *
97       * @since 2.0.15
98       * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
99       * @configurationType {@link java.lang.String}
100      * @configurationDefaultValue {@link #DEFAULT_CHECKSUMS_ALGORITHMS}
101      * @configurationRepoIdSuffix Yes
102      */
103     public static final String CONFIG_PROP_DOWNLOAD_CHECKSUMS_ALGORITHMS =
104             ConfigurationProperties.PREFIX_CHECKSUMS + "downloadChecksumAlgorithms";
105 
106     private float priority;
107 
108     private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector;
109 
110     private final ArtifactPredicateFactory artifactPredicateFactory;
111 
112     public float getPriority() {
113         return priority;
114     }
115 
116     @Inject
117     public Maven2RepositoryLayoutFactory(
118             ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector,
119             ArtifactPredicateFactory artifactPredicateFactory) {
120         this.checksumAlgorithmFactorySelector = requireNonNull(checksumAlgorithmFactorySelector);
121         this.artifactPredicateFactory = requireNonNull(artifactPredicateFactory);
122     }
123 
124     /**
125      * Sets the priority of this component.
126      *
127      * @param priority The priority.
128      * @return This component for chaining, never {@code null}.
129      */
130     public Maven2RepositoryLayoutFactory setPriority(float priority) {
131         this.priority = priority;
132         return this;
133     }
134 
135     public RepositoryLayout newInstance(RepositorySystemSession session, RemoteRepository repository)
136             throws NoRepositoryLayoutException {
137         requireNonNull(session, "session cannot be null");
138         requireNonNull(repository, "repository cannot be null");
139         if (!"default".equals(repository.getContentType())) {
140             throw new NoRepositoryLayoutException(repository);
141         }
142 
143         // explicit property for download (will be empty if not configured)
144         List<String> downloadChecksumsAlgorithmNames = ConfigUtils.parseCommaSeparatedUniqueNames(ConfigUtils.getString(
145                 session,
146                 DEFAULT_CHECKSUMS_ALGORITHMS,
147                 CONFIG_PROP_DOWNLOAD_CHECKSUMS_ALGORITHMS + "." + repository.getId(),
148                 CONFIG_PROP_DOWNLOAD_CHECKSUMS_ALGORITHMS,
149                 CONFIG_PROP_CHECKSUMS_ALGORITHMS + "." + repository.getId(),
150                 CONFIG_PROP_CHECKSUMS_ALGORITHMS,
151                 // MRESOLVER-701: support legacy properties for simpler transitioning
152                 "aether.checksums.algorithms." + repository.getId(),
153                 "aether.checksums.algorithms"));
154 
155         // explicit property for upload (will be empty if not configured)
156         List<String> uploadChecksumsAlgorithmNames = ConfigUtils.parseCommaSeparatedUniqueNames(ConfigUtils.getString(
157                 session,
158                 DEFAULT_CHECKSUMS_ALGORITHMS,
159                 CONFIG_PROP_UPLOAD_CHECKSUMS_ALGORITHMS + "." + repository.getId(),
160                 CONFIG_PROP_UPLOAD_CHECKSUMS_ALGORITHMS,
161                 CONFIG_PROP_CHECKSUMS_ALGORITHMS + "." + repository.getId(),
162                 CONFIG_PROP_CHECKSUMS_ALGORITHMS,
163                 // MRESOLVER-701: support legacy properties for simpler transitioning
164                 "aether.checksums.algorithms." + repository.getId(),
165                 "aether.checksums.algorithms"));
166 
167         return new Maven2RepositoryLayout(
168                 checksumAlgorithmFactorySelector.selectList(downloadChecksumsAlgorithmNames),
169                 checksumAlgorithmFactorySelector.selectList(uploadChecksumsAlgorithmNames),
170                 artifactPredicateFactory.newInstance(session));
171     }
172 
173     private static class Maven2RepositoryLayout implements RepositoryLayout {
174         private final List<ChecksumAlgorithmFactory> configuredDownloadChecksumAlgorithms;
175         private final List<ChecksumAlgorithmFactory> configuredUploadChecksumAlgorithms;
176         private final ArtifactPredicate artifactPredicate;
177 
178         private Maven2RepositoryLayout(
179                 List<ChecksumAlgorithmFactory> configuredDownloadChecksumAlgorithms,
180                 List<ChecksumAlgorithmFactory> configuredUploadChecksumAlgorithms,
181                 ArtifactPredicate artifactPredicate) {
182             this.configuredDownloadChecksumAlgorithms =
183                     Collections.unmodifiableList(configuredDownloadChecksumAlgorithms);
184             this.configuredUploadChecksumAlgorithms = Collections.unmodifiableList(configuredUploadChecksumAlgorithms);
185             this.artifactPredicate = requireNonNull(artifactPredicate);
186         }
187 
188         private URI toUri(String path) {
189             try {
190                 return new URI(null, null, path, null);
191             } catch (URISyntaxException e) {
192                 throw new IllegalStateException(e);
193             }
194         }
195 
196         @Override
197         public List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories() {
198             LinkedHashMap<String, ChecksumAlgorithmFactory> factories = new LinkedHashMap<>();
199             configuredDownloadChecksumAlgorithms.forEach(f -> factories.putIfAbsent(f.getName(), f));
200             configuredUploadChecksumAlgorithms.forEach(f -> factories.putIfAbsent(f.getName(), f));
201             return Collections.unmodifiableList(new ArrayList<>(factories.values()));
202         }
203 
204         @Override
205         public List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories(boolean upload) {
206             if (upload) {
207                 return configuredUploadChecksumAlgorithms;
208             } else {
209                 return configuredDownloadChecksumAlgorithms;
210             }
211         }
212 
213         @Override
214         public boolean hasChecksums(Artifact artifact) {
215             return !artifactPredicate.isWithoutChecksum(artifact);
216         }
217 
218         @Override
219         public URI getLocation(Artifact artifact, boolean upload) {
220             StringBuilder path = new StringBuilder(128);
221 
222             path.append(artifact.getGroupId().replace('.', '/')).append('/');
223 
224             path.append(artifact.getArtifactId()).append('/');
225 
226             path.append(artifact.getBaseVersion()).append('/');
227 
228             path.append(artifact.getArtifactId()).append('-').append(artifact.getVersion());
229 
230             if (!artifact.getClassifier().isEmpty()) {
231                 path.append('-').append(artifact.getClassifier());
232             }
233 
234             if (!artifact.getExtension().isEmpty()) {
235                 path.append('.').append(artifact.getExtension());
236             }
237 
238             return toUri(path.toString());
239         }
240 
241         @Override
242         public URI getLocation(Metadata metadata, boolean upload) {
243             StringBuilder path = new StringBuilder(128);
244 
245             if (!metadata.getGroupId().isEmpty()) {
246                 path.append(metadata.getGroupId().replace('.', '/')).append('/');
247 
248                 if (!metadata.getArtifactId().isEmpty()) {
249                     path.append(metadata.getArtifactId()).append('/');
250 
251                     if (!metadata.getVersion().isEmpty()) {
252                         path.append(metadata.getVersion()).append('/');
253                     }
254                 }
255             }
256 
257             path.append(metadata.getType());
258 
259             return toUri(path.toString());
260         }
261 
262         @Override
263         public List<ChecksumLocation> getChecksumLocations(Artifact artifact, boolean upload, URI location) {
264             if (artifactPredicate.isWithoutChecksum(artifact) || artifactPredicate.isChecksum(artifact)) {
265                 return Collections.emptyList();
266             }
267             return getChecksumLocations(location, upload);
268         }
269 
270         @Override
271         public List<ChecksumLocation> getChecksumLocations(Metadata metadata, boolean upload, URI location) {
272             return getChecksumLocations(location, upload);
273         }
274 
275         private List<ChecksumLocation> getChecksumLocations(URI location, boolean upload) {
276             List<ChecksumAlgorithmFactory> checksumAlgorithmFactories =
277                     upload ? configuredUploadChecksumAlgorithms : configuredDownloadChecksumAlgorithms;
278             List<ChecksumLocation> checksumLocations = new ArrayList<>(checksumAlgorithmFactories.size());
279             for (ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories) {
280                 checksumLocations.add(ChecksumLocation.forLocation(location, checksumAlgorithmFactory));
281             }
282             return checksumLocations;
283         }
284     }
285 }