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.Arrays; 029import java.util.Collections; 030import java.util.List; 031import java.util.Set; 032import java.util.stream.Collectors; 033 034import org.eclipse.aether.RepositorySystemSession; 035import org.eclipse.aether.artifact.Artifact; 036import org.eclipse.aether.internal.impl.checksum.DefaultChecksumAlgorithmFactorySelector; 037import org.eclipse.aether.metadata.Metadata; 038import org.eclipse.aether.repository.RemoteRepository; 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; 047 048/** 049 * Provides a Maven-2 repository layout for repositories with content type {@code "default"}. 050 */ 051@Singleton 052@Named(Maven2RepositoryLayoutFactory.NAME) 053public final class Maven2RepositoryLayoutFactory implements RepositoryLayoutFactory { 054 public static final String NAME = "maven2"; 055 056 public static final String CONFIG_PROP_CHECKSUMS_ALGORITHMS = "aether.checksums.algorithms"; 057 058 private static final String DEFAULT_CHECKSUMS_ALGORITHMS = "SHA-1,MD5"; 059 060 public static final String CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS = 061 "aether.checksums.omitChecksumsForExtensions"; 062 063 private static final String DEFAULT_OMIT_CHECKSUMS_FOR_EXTENSIONS = ".asc,.sigstore"; 064 065 private float priority; 066 067 private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector; 068 069 public float getPriority() { 070 return priority; 071 } 072 073 /** 074 * Service locator ctor. 075 */ 076 @Deprecated 077 public Maven2RepositoryLayoutFactory() { 078 this(new DefaultChecksumAlgorithmFactorySelector()); 079 } 080 081 @Inject 082 public Maven2RepositoryLayoutFactory(ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector) { 083 this.checksumAlgorithmFactorySelector = requireNonNull(checksumAlgorithmFactorySelector); 084 } 085 086 /** 087 * Sets the priority of this component. 088 * 089 * @param priority The priority. 090 * @return This component for chaining, never {@code null}. 091 */ 092 public Maven2RepositoryLayoutFactory setPriority(float priority) { 093 this.priority = priority; 094 return this; 095 } 096 097 public RepositoryLayout newInstance(RepositorySystemSession session, RemoteRepository repository) 098 throws NoRepositoryLayoutException { 099 requireNonNull(session, "session cannot be null"); 100 requireNonNull(repository, "repository cannot be null"); 101 if (!"default".equals(repository.getContentType())) { 102 throw new NoRepositoryLayoutException(repository); 103 } 104 105 List<ChecksumAlgorithmFactory> checksumsAlgorithms = checksumAlgorithmFactorySelector.selectList( 106 ConfigUtils.parseCommaSeparatedUniqueNames(ConfigUtils.getString( 107 session, 108 DEFAULT_CHECKSUMS_ALGORITHMS, 109 CONFIG_PROP_CHECKSUMS_ALGORITHMS + "." + repository.getId(), 110 CONFIG_PROP_CHECKSUMS_ALGORITHMS))); 111 112 // ensure uniqueness of (potentially user set) extension list 113 Set<String> omitChecksumsForExtensions = Arrays.stream(ConfigUtils.getString( 114 session, 115 DEFAULT_OMIT_CHECKSUMS_FOR_EXTENSIONS, 116 CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS) 117 .split(",")) 118 .filter(s -> s != null && !s.trim().isEmpty()) 119 .collect(Collectors.toSet()); 120 121 // validation: enforce that all strings in this set are having leading dot 122 if (omitChecksumsForExtensions.stream().anyMatch(s -> !s.startsWith("."))) { 123 throw new IllegalArgumentException(String.format( 124 "The configuration %s contains illegal values: %s (all entries must start with '.' (dot))", 125 CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS, omitChecksumsForExtensions)); 126 } 127 128 return new Maven2RepositoryLayout( 129 checksumAlgorithmFactorySelector, checksumsAlgorithms, omitChecksumsForExtensions); 130 } 131 132 private static class Maven2RepositoryLayout implements RepositoryLayout { 133 private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector; 134 135 private final List<ChecksumAlgorithmFactory> configuredChecksumAlgorithms; 136 137 private final Set<String> extensionsWithoutChecksums; 138 139 private Maven2RepositoryLayout( 140 ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector, 141 List<ChecksumAlgorithmFactory> configuredChecksumAlgorithms, 142 Set<String> extensionsWithoutChecksums) { 143 this.checksumAlgorithmFactorySelector = requireNonNull(checksumAlgorithmFactorySelector); 144 this.configuredChecksumAlgorithms = Collections.unmodifiableList(configuredChecksumAlgorithms); 145 this.extensionsWithoutChecksums = requireNonNull(extensionsWithoutChecksums); 146 } 147 148 private URI toUri(String path) { 149 try { 150 return new URI(null, null, path, null); 151 } catch (URISyntaxException e) { 152 throw new IllegalStateException(e); 153 } 154 } 155 156 @Override 157 public List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories() { 158 return configuredChecksumAlgorithms; 159 } 160 161 @Override 162 public boolean hasChecksums(Artifact artifact) { 163 String artifactExtension = artifact.getExtension(); // ie. pom.asc 164 for (String extensionWithoutChecksums : extensionsWithoutChecksums) { 165 if (artifactExtension.endsWith(extensionWithoutChecksums)) { 166 return false; 167 } 168 } 169 return true; 170 } 171 172 @Override 173 public URI getLocation(Artifact artifact, boolean upload) { 174 StringBuilder path = new StringBuilder(128); 175 176 path.append(artifact.getGroupId().replace('.', '/')).append('/'); 177 178 path.append(artifact.getArtifactId()).append('/'); 179 180 path.append(artifact.getBaseVersion()).append('/'); 181 182 path.append(artifact.getArtifactId()).append('-').append(artifact.getVersion()); 183 184 if (artifact.getClassifier().length() > 0) { 185 path.append('-').append(artifact.getClassifier()); 186 } 187 188 if (artifact.getExtension().length() > 0) { 189 path.append('.').append(artifact.getExtension()); 190 } 191 192 return toUri(path.toString()); 193 } 194 195 @Override 196 public URI getLocation(Metadata metadata, boolean upload) { 197 StringBuilder path = new StringBuilder(128); 198 199 if (metadata.getGroupId().length() > 0) { 200 path.append(metadata.getGroupId().replace('.', '/')).append('/'); 201 202 if (metadata.getArtifactId().length() > 0) { 203 path.append(metadata.getArtifactId()).append('/'); 204 205 if (metadata.getVersion().length() > 0) { 206 path.append(metadata.getVersion()).append('/'); 207 } 208 } 209 } 210 211 path.append(metadata.getType()); 212 213 return toUri(path.toString()); 214 } 215 216 @Override 217 public List<ChecksumLocation> getChecksumLocations(Artifact artifact, boolean upload, URI location) { 218 if (!hasChecksums(artifact) || isChecksum(artifact.getExtension())) { 219 return Collections.emptyList(); 220 } 221 return getChecksumLocations(location); 222 } 223 224 @Override 225 public List<ChecksumLocation> getChecksumLocations(Metadata metadata, boolean upload, URI location) { 226 return getChecksumLocations(location); 227 } 228 229 private List<ChecksumLocation> getChecksumLocations(URI location) { 230 List<ChecksumLocation> checksumLocations = new ArrayList<>(configuredChecksumAlgorithms.size()); 231 for (ChecksumAlgorithmFactory checksumAlgorithmFactory : configuredChecksumAlgorithms) { 232 checksumLocations.add(ChecksumLocation.forLocation(location, checksumAlgorithmFactory)); 233 } 234 return checksumLocations; 235 } 236 237 private boolean isChecksum(String extension) { 238 return checksumAlgorithmFactorySelector.isChecksumExtension(extension); 239 } 240 } 241}