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