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