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 org.eclipse.aether.RepositorySystemSession; 023import org.eclipse.aether.spi.connector.ArtifactDownload; 024import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; 025import org.eclipse.aether.spi.connector.checksum.ProvidedChecksumsSource; 026import org.eclipse.aether.spi.io.FileProcessor; 027import org.eclipse.aether.util.ConfigUtils; 028import org.eclipse.aether.util.artifact.ArtifactIdUtils; 029import org.slf4j.Logger; 030import org.slf4j.LoggerFactory; 031 032import javax.inject.Inject; 033import javax.inject.Named; 034import javax.inject.Singleton; 035import java.io.File; 036import java.io.IOException; 037import java.net.URI; 038import java.nio.file.Files; 039import java.nio.file.Path; 040import java.nio.file.Paths; 041import java.util.ArrayList; 042import java.util.HashMap; 043import java.util.List; 044import java.util.Map; 045 046import static java.util.Objects.requireNonNull; 047 048/** 049 * Local filesystem backed {@link ProvidedChecksumsSource} implementation that use specified directory as base 050 * directory, where it expects artifacts checksums on standard Maven2 "local" layout. This implementation uses Artifact 051 * (and Metadata) coordinates solely to form path from baseDir (for Metadata file name is 052 * {@code maven-metadata-local.xml.sha1} in case of SHA-1 checksum). 053 * 054 * @since 1.8.0 055 */ 056@Singleton 057@Named( FileProvidedChecksumsSource.NAME ) 058public final class FileProvidedChecksumsSource 059 implements ProvidedChecksumsSource 060{ 061 public static final String NAME = "file"; 062 063 static final String CONFIG_PROP_BASE_DIR = "aether.artifactResolver.providedChecksumsSource.file.baseDir"; 064 065 static final String LOCAL_REPO_PREFIX = ".checksums"; 066 067 private static final Logger LOGGER = LoggerFactory.getLogger( FileProvidedChecksumsSource.class ); 068 069 private final FileProcessor fileProcessor; 070 071 private final SimpleLocalRepositoryManager simpleLocalRepositoryManager; 072 073 @Inject 074 public FileProvidedChecksumsSource( FileProcessor fileProcessor ) 075 { 076 this.fileProcessor = requireNonNull( fileProcessor ); 077 // we really needs just "local layout" from it (relative paths), so baseDir here is irrelevant 078 this.simpleLocalRepositoryManager = new SimpleLocalRepositoryManager( new File( "" ) ); 079 } 080 081 @Override 082 public Map<String, String> getProvidedArtifactChecksums( RepositorySystemSession session, 083 ArtifactDownload transfer, 084 List<ChecksumAlgorithmFactory> checksumAlgorithmFactories ) 085 { 086 Path baseDir = getBaseDir( session ); 087 if ( baseDir == null ) 088 { 089 return null; 090 } 091 ArrayList<ChecksumFilePath> checksumFilePaths = new ArrayList<>( checksumAlgorithmFactories.size() ); 092 for ( ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories ) 093 { 094 checksumFilePaths.add( new ChecksumFilePath( 095 simpleLocalRepositoryManager.getPathForArtifact( transfer.getArtifact(), false ) + '.' 096 + checksumAlgorithmFactory.getFileExtension(), checksumAlgorithmFactory ) ); 097 } 098 return getProvidedChecksums( baseDir, checksumFilePaths, ArtifactIdUtils.toId( transfer.getArtifact() ) ); 099 } 100 101 /** 102 * May return {@code null}. 103 */ 104 private Map<String, String> getProvidedChecksums( Path baseDir, 105 List<ChecksumFilePath> checksumFilePaths, 106 String subjectId ) 107 { 108 HashMap<String, String> checksums = new HashMap<>(); 109 for ( ChecksumFilePath checksumFilePath : checksumFilePaths ) 110 { 111 Path checksumPath = baseDir.resolve( checksumFilePath.path ); 112 if ( Files.isReadable( checksumPath ) ) 113 { 114 try 115 { 116 String checksum = fileProcessor.readChecksum( checksumPath.toFile() ); 117 if ( checksum != null ) 118 { 119 LOGGER.debug( "Resolved provided checksum '{}:{}' for '{}'", 120 checksumFilePath.checksumAlgorithmFactory.getName(), checksum, subjectId ); 121 122 checksums.put( checksumFilePath.checksumAlgorithmFactory.getName(), checksum ); 123 } 124 } 125 catch ( IOException e ) 126 { 127 LOGGER.warn( "Could not read provided checksum for '{}' at path '{}'", 128 subjectId, checksumPath, e ); 129 } 130 } 131 } 132 return checksums.isEmpty() ? null : checksums; 133 } 134 135 /** 136 * Returns the base {@link URI} of directory where checksums are laid out, may return {@code null}. 137 */ 138 private Path getBaseDir( RepositorySystemSession session ) 139 { 140 final String baseDirPath = ConfigUtils.getString( session, null, CONFIG_PROP_BASE_DIR ); 141 final Path baseDir; 142 if ( baseDirPath != null ) 143 { 144 baseDir = Paths.get( baseDirPath ); 145 } 146 else 147 { 148 baseDir = session.getLocalRepository().getBasedir().toPath().resolve( LOCAL_REPO_PREFIX ); 149 } 150 if ( !Files.isDirectory( baseDir ) ) 151 { 152 return null; 153 } 154 return baseDir; 155 } 156 157 private static final class ChecksumFilePath 158 { 159 private final String path; 160 161 private final ChecksumAlgorithmFactory checksumAlgorithmFactory; 162 163 private ChecksumFilePath( String path, ChecksumAlgorithmFactory checksumAlgorithmFactory ) 164 { 165 this.path = path; 166 this.checksumAlgorithmFactory = checksumAlgorithmFactory; 167 } 168 } 169}