001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.internal.impl; 020 021import javax.inject.Inject; 022import javax.inject.Named; 023import javax.inject.Singleton; 024 025import java.net.URI; 026import java.net.URISyntaxException; 027import java.util.ArrayList; 028import java.util.Collections; 029import java.util.LinkedHashMap; 030import java.util.List; 031 032import org.eclipse.aether.ConfigurationProperties; 033import org.eclipse.aether.RepositorySystemSession; 034import org.eclipse.aether.artifact.Artifact; 035import org.eclipse.aether.metadata.Metadata; 036import org.eclipse.aether.repository.RemoteRepository; 037import org.eclipse.aether.spi.artifact.ArtifactPredicate; 038import org.eclipse.aether.spi.artifact.ArtifactPredicateFactory; 039import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; 040import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector; 041import org.eclipse.aether.spi.connector.layout.RepositoryLayout; 042import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory; 043import org.eclipse.aether.transfer.NoRepositoryLayoutException; 044import org.eclipse.aether.util.ConfigUtils; 045 046import static java.util.Objects.requireNonNull; 047import static org.eclipse.aether.util.PathUtils.validateArtifactComponents; 048import static org.eclipse.aether.util.PathUtils.validateMetadataComponents; 049 050/** 051 * Provides a Maven-2 repository layout for repositories with content type {@code "default"}. 052 */ 053@Singleton 054@Named(Maven2RepositoryLayoutFactory.NAME) 055public final class Maven2RepositoryLayoutFactory implements RepositoryLayoutFactory { 056 public static final String NAME = "maven2"; 057 058 /** 059 * Comma-separated list of checksum algorithms with which checksums are validated (downloaded) and generated 060 * (uploaded) with this layout. Resolver by default supports following algorithms: MD5, SHA-1, SHA-256 and 061 * SHA-512. New algorithms can be added by implementing ChecksumAlgorithmFactory component. To configure separately 062 * checksums for download or upload, use {@link #CONFIG_PROP_DOWNLOAD_CHECKSUMS_ALGORITHMS} and 063 * {@link #CONFIG_PROP_UPLOAD_CHECKSUMS_ALGORITHMS} respectively. 064 * 065 * @since 1.8.0 066 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 067 * @configurationType {@link java.lang.String} 068 * @configurationDefaultValue {@link #DEFAULT_CHECKSUMS_ALGORITHMS} 069 * @configurationRepoIdSuffix Yes 070 */ 071 public static final String CONFIG_PROP_CHECKSUMS_ALGORITHMS = 072 ConfigurationProperties.PREFIX_CHECKSUMS + "checksumAlgorithms"; 073 074 public static final String DEFAULT_CHECKSUMS_ALGORITHMS = "SHA-1,MD5"; 075 076 /** 077 * Comma-separated list of checksum algorithms with which checksums are generated and uploaded 078 * with this layout. Resolver by default supports following algorithms: MD5, SHA-1, SHA-256 and 079 * SHA-512. New algorithms can be added by implementing ChecksumAlgorithmFactory component. 080 * If this property is set, it <em>overrides</em> the value set in {@link #CONFIG_PROP_CHECKSUMS_ALGORITHMS} for 081 * uploads. 082 * 083 * @since 2.0.15 084 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 085 * @configurationType {@link java.lang.String} 086 * @configurationDefaultValue {@link #DEFAULT_CHECKSUMS_ALGORITHMS} 087 * @configurationRepoIdSuffix Yes 088 */ 089 public static final String CONFIG_PROP_UPLOAD_CHECKSUMS_ALGORITHMS = 090 ConfigurationProperties.PREFIX_CHECKSUMS + "uploadChecksumAlgorithms"; 091 092 /** 093 * Comma-separated list of checksum algorithms with which checksums are validated (downloaded) with this layout. 094 * Resolver by default supports following algorithms: MD5, SHA-1, SHA-256 and SHA-512. 095 * New algorithms can be added by implementing ChecksumAlgorithmFactory component. 096 * If this property is set, it <em>overrides</em> the value set in {@link #CONFIG_PROP_CHECKSUMS_ALGORITHMS} for 097 * downloads. 098 * 099 * @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}