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