001package org.eclipse.aether.internal.impl; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import java.net.URI; 023import java.net.URISyntaxException; 024import java.util.ArrayList; 025import java.util.Arrays; 026import java.util.Collections; 027import java.util.List; 028import java.util.Set; 029import java.util.stream.Collectors; 030 031import javax.inject.Inject; 032import javax.inject.Named; 033import javax.inject.Singleton; 034 035import org.eclipse.aether.RepositorySystemSession; 036import org.eclipse.aether.artifact.Artifact; 037import org.eclipse.aether.internal.impl.checksum.DefaultChecksumAlgorithmFactorySelector; 038import org.eclipse.aether.metadata.Metadata; 039import org.eclipse.aether.repository.RemoteRepository; 040import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; 041import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector; 042import org.eclipse.aether.spi.connector.layout.RepositoryLayout; 043import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory; 044import org.eclipse.aether.transfer.NoRepositoryLayoutException; 045import org.eclipse.aether.util.ConfigUtils; 046 047import static java.util.Objects.requireNonNull; 048 049/** 050 * Provides a Maven-2 repository layout for repositories with content type {@code "default"}. 051 */ 052@Singleton 053@Named( "maven2" ) 054public final class Maven2RepositoryLayoutFactory 055 implements RepositoryLayoutFactory 056{ 057 058 public static final String CONFIG_PROP_CHECKSUMS_ALGORITHMS = "aether.checksums.algorithms"; 059 060 private static final String DEFAULT_CHECKSUMS_ALGORITHMS = "SHA-1,MD5"; 061 062 public static final String CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS = 063 "aether.checksums.omitChecksumsForExtensions"; 064 065 private static final String DEFAULT_OMIT_CHECKSUMS_FOR_EXTENSIONS = ".asc"; 066 067 private float priority; 068 069 private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector; 070 071 public float getPriority() 072 { 073 return priority; 074 } 075 076 /** 077 * Service locator ctor. 078 */ 079 @Deprecated 080 public Maven2RepositoryLayoutFactory() 081 { 082 this( new DefaultChecksumAlgorithmFactorySelector() ); 083 } 084 085 @Inject 086 public Maven2RepositoryLayoutFactory( ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector ) 087 { 088 this.checksumAlgorithmFactorySelector = requireNonNull( checksumAlgorithmFactorySelector ); 089 } 090 091 /** 092 * Sets the priority of this component. 093 * 094 * @param priority The priority. 095 * @return This component for chaining, never {@code null}. 096 */ 097 public Maven2RepositoryLayoutFactory setPriority( float priority ) 098 { 099 this.priority = priority; 100 return this; 101 } 102 103 public RepositoryLayout newInstance( RepositorySystemSession session, RemoteRepository repository ) 104 throws NoRepositoryLayoutException 105 { 106 requireNonNull( session, "session cannot be null" ); 107 requireNonNull( repository, "repository cannot be null" ); 108 if ( !"default".equals( repository.getContentType() ) ) 109 { 110 throw new NoRepositoryLayoutException( repository ); 111 } 112 113 List<ChecksumAlgorithmFactory> checksumsAlgorithms = checksumAlgorithmFactorySelector.selectList( 114 ConfigUtils.parseCommaSeparatedUniqueNames( ConfigUtils.getString( 115 session, DEFAULT_CHECKSUMS_ALGORITHMS, CONFIG_PROP_CHECKSUMS_ALGORITHMS ) ) 116 ); 117 118 // ensure uniqueness of (potentially user set) extension list 119 Set<String> omitChecksumsForExtensions = Arrays.stream( ConfigUtils.getString( 120 session, DEFAULT_OMIT_CHECKSUMS_FOR_EXTENSIONS, CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS ) 121 .split( "," ) 122 ).filter( s -> s != null && !s.trim().isEmpty() ).collect( Collectors.toSet() ); 123 124 // validation: enforce that all strings in this set are having leading dot 125 if ( omitChecksumsForExtensions.stream().anyMatch( s -> !s.startsWith( "." ) ) ) 126 { 127 throw new IllegalArgumentException( 128 String.format( 129 "The configuration %s contains illegal values: %s (all entries must start with '.' (dot))", 130 CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS, 131 omitChecksumsForExtensions 132 ) 133 ); 134 } 135 136 return new Maven2RepositoryLayout( 137 checksumAlgorithmFactorySelector, 138 checksumsAlgorithms, 139 omitChecksumsForExtensions 140 ); 141 } 142 143 private static class Maven2RepositoryLayout 144 implements RepositoryLayout 145 { 146 private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector; 147 148 private final List<ChecksumAlgorithmFactory> configuredChecksumAlgorithms; 149 150 private final Set<String> extensionsWithoutChecksums; 151 152 private Maven2RepositoryLayout( ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector, 153 List<ChecksumAlgorithmFactory> configuredChecksumAlgorithms, 154 Set<String> extensionsWithoutChecksums ) 155 { 156 this.checksumAlgorithmFactorySelector = requireNonNull( checksumAlgorithmFactorySelector ); 157 this.configuredChecksumAlgorithms = Collections.unmodifiableList( configuredChecksumAlgorithms ); 158 this.extensionsWithoutChecksums = requireNonNull( extensionsWithoutChecksums ); 159 } 160 161 private URI toUri( String path ) 162 { 163 try 164 { 165 return new URI( null, null, path, null ); 166 } 167 catch ( URISyntaxException e ) 168 { 169 throw new IllegalStateException( e ); 170 } 171 } 172 173 @Override 174 public List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories() 175 { 176 return configuredChecksumAlgorithms; 177 } 178 179 @Override 180 public boolean hasChecksums( Artifact artifact ) 181 { 182 String artifactExtension = artifact.getExtension(); // ie. pom.asc 183 for ( String extensionWithoutChecksums : extensionsWithoutChecksums ) 184 { 185 if ( artifactExtension.endsWith( extensionWithoutChecksums ) ) 186 { 187 return false; 188 } 189 } 190 return true; 191 } 192 193 @Override 194 public URI getLocation( Artifact artifact, boolean upload ) 195 { 196 StringBuilder path = new StringBuilder( 128 ); 197 198 path.append( artifact.getGroupId().replace( '.', '/' ) ).append( '/' ); 199 200 path.append( artifact.getArtifactId() ).append( '/' ); 201 202 path.append( artifact.getBaseVersion() ).append( '/' ); 203 204 path.append( artifact.getArtifactId() ).append( '-' ).append( artifact.getVersion() ); 205 206 if ( artifact.getClassifier().length() > 0 ) 207 { 208 path.append( '-' ).append( artifact.getClassifier() ); 209 } 210 211 if ( artifact.getExtension().length() > 0 ) 212 { 213 path.append( '.' ).append( artifact.getExtension() ); 214 } 215 216 return toUri( path.toString() ); 217 } 218 219 @Override 220 public URI getLocation( Metadata metadata, boolean upload ) 221 { 222 StringBuilder path = new StringBuilder( 128 ); 223 224 if ( metadata.getGroupId().length() > 0 ) 225 { 226 path.append( metadata.getGroupId().replace( '.', '/' ) ).append( '/' ); 227 228 if ( metadata.getArtifactId().length() > 0 ) 229 { 230 path.append( metadata.getArtifactId() ).append( '/' ); 231 232 if ( metadata.getVersion().length() > 0 ) 233 { 234 path.append( metadata.getVersion() ).append( '/' ); 235 } 236 } 237 } 238 239 path.append( metadata.getType() ); 240 241 return toUri( path.toString() ); 242 } 243 244 @Override 245 public List<ChecksumLocation> getChecksumLocations( Artifact artifact, boolean upload, URI location ) 246 { 247 if ( !hasChecksums( artifact ) || isChecksum( artifact.getExtension() ) ) 248 { 249 return Collections.emptyList(); 250 } 251 return getChecksumLocations( location ); 252 } 253 254 @Override 255 public List<ChecksumLocation> getChecksumLocations( Metadata metadata, boolean upload, URI location ) 256 { 257 return getChecksumLocations( location ); 258 } 259 260 private List<ChecksumLocation> getChecksumLocations( URI location ) 261 { 262 List<ChecksumLocation> checksumLocations = new ArrayList<>( configuredChecksumAlgorithms.size() ); 263 for ( ChecksumAlgorithmFactory checksumAlgorithmFactory : configuredChecksumAlgorithms ) 264 { 265 checksumLocations.add( ChecksumLocation.forLocation( location, checksumAlgorithmFactory ) ); 266 } 267 return checksumLocations; 268 } 269 270 private boolean isChecksum( String extension ) 271 { 272 return checksumAlgorithmFactorySelector.isChecksumExtension( extension ); 273 } 274 } 275}