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.checksum; 020 021import javax.inject.Inject; 022import javax.inject.Named; 023import javax.inject.Singleton; 024 025import java.io.IOException; 026import java.io.UncheckedIOException; 027import java.nio.file.Files; 028import java.nio.file.Path; 029import java.util.HashMap; 030import java.util.List; 031import java.util.Map; 032import java.util.function.Function; 033 034import org.eclipse.aether.RepositorySystemSession; 035import org.eclipse.aether.artifact.Artifact; 036import org.eclipse.aether.internal.impl.LocalPathComposer; 037import org.eclipse.aether.repository.ArtifactRepository; 038import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory; 039import org.eclipse.aether.spi.io.ChecksumProcessor; 040import org.eclipse.aether.util.ConfigUtils; 041import org.eclipse.aether.util.repository.RepositoryIdHelper; 042import org.slf4j.Logger; 043import org.slf4j.LoggerFactory; 044 045import static java.util.Objects.requireNonNull; 046 047/** 048 * Sparse file {@link FileTrustedChecksumsSourceSupport} implementation that use specified directory as base 049 * directory, where it expects artifacts checksums on standard Maven2 "local" layout. This implementation uses Artifact 050 * coordinates solely to form path from basedir, pretty much as Maven local repository does. 051 * <p> 052 * The source by default is "origin aware", it will factor in origin repository ID as well into base directory name 053 * (for example ".checksums/central/..."). 054 * <p> 055 * The checksums files are directly loaded from disk, so in-flight file changes during lifecycle of session are picked 056 * up. This implementation can be simultaneously used to lookup and also write checksums. The written checksums 057 * will become visible across all sessions right after the moment they were written. 058 * <p> 059 * The name of this implementation is "sparseDirectory". 060 * 061 * @see LocalPathComposer 062 * @since 1.9.0 063 */ 064@Singleton 065@Named(SparseDirectoryTrustedChecksumsSource.NAME) 066public final class SparseDirectoryTrustedChecksumsSource extends FileTrustedChecksumsSourceSupport { 067 public static final String NAME = "sparseDirectory"; 068 069 private static final String CONFIG_PROPS_PREFIX = 070 FileTrustedChecksumsSourceSupport.CONFIG_PROPS_PREFIX + NAME + "."; 071 072 /** 073 * Is checksum source enabled? 074 * 075 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 076 * @configurationType {@link java.lang.Boolean} 077 * @configurationDefaultValue false 078 */ 079 public static final String CONFIG_PROP_ENABLED = FileTrustedChecksumsSourceSupport.CONFIG_PROPS_PREFIX + NAME; 080 081 /** 082 * The basedir where checksums are. If relative, is resolved from local repository root. 083 * 084 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 085 * @configurationType {@link java.lang.String} 086 * @configurationDefaultValue {@link #LOCAL_REPO_PREFIX_DIR} 087 */ 088 public static final String CONFIG_PROP_BASEDIR = CONFIG_PROPS_PREFIX + "basedir"; 089 090 public static final String LOCAL_REPO_PREFIX_DIR = ".checksums"; 091 092 /** 093 * Is source origin aware? 094 * 095 * @configurationSource {@link RepositorySystemSession#getConfigProperties()} 096 * @configurationType {@link java.lang.Boolean} 097 * @configurationDefaultValue true 098 */ 099 public static final String CONFIG_PROP_ORIGIN_AWARE = CONFIG_PROPS_PREFIX + "originAware"; 100 101 private static final Logger LOGGER = LoggerFactory.getLogger(SparseDirectoryTrustedChecksumsSource.class); 102 103 private final ChecksumProcessor checksumProcessor; 104 105 private final LocalPathComposer localPathComposer; 106 107 @Inject 108 public SparseDirectoryTrustedChecksumsSource( 109 ChecksumProcessor checksumProcessor, LocalPathComposer localPathComposer) { 110 this.checksumProcessor = requireNonNull(checksumProcessor); 111 this.localPathComposer = requireNonNull(localPathComposer); 112 } 113 114 @Override 115 protected boolean isEnabled(RepositorySystemSession session) { 116 return ConfigUtils.getBoolean(session, false, CONFIG_PROP_ENABLED); 117 } 118 119 private boolean isOriginAware(RepositorySystemSession session) { 120 return ConfigUtils.getBoolean(session, true, CONFIG_PROP_ORIGIN_AWARE); 121 } 122 123 @Override 124 protected Map<String, String> doGetTrustedArtifactChecksums( 125 RepositorySystemSession session, 126 Artifact artifact, 127 ArtifactRepository artifactRepository, 128 List<ChecksumAlgorithmFactory> checksumAlgorithmFactories) { 129 final boolean originAware = isOriginAware(session); 130 final HashMap<String, String> checksums = new HashMap<>(); 131 Path basedir = getBasedir(session, LOCAL_REPO_PREFIX_DIR, CONFIG_PROP_BASEDIR, false); 132 if (Files.isDirectory(basedir)) { 133 for (ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories) { 134 Path checksumPath = basedir.resolve(calculateArtifactPath( 135 originAware, 136 artifact, 137 RepositoryIdHelper.cachedIdToPathSegment(session).apply(artifactRepository), 138 checksumAlgorithmFactory)); 139 140 if (!Files.isRegularFile(checksumPath)) { 141 LOGGER.debug( 142 "Artifact '{}' trusted checksum '{}' not found on path '{}'", 143 artifact, 144 checksumAlgorithmFactory.getName(), 145 checksumPath); 146 continue; 147 } 148 149 try { 150 String checksum = checksumProcessor.readChecksum(checksumPath); 151 if (checksum != null) { 152 checksums.put(checksumAlgorithmFactory.getName(), checksum); 153 } 154 } catch (IOException e) { 155 // unexpected, log 156 LOGGER.warn( 157 "Could not read artifact '{}' trusted checksum on path '{}'", artifact, checksumPath, e); 158 throw new UncheckedIOException(e); 159 } 160 } 161 } 162 return checksums; 163 } 164 165 @Override 166 protected Writer doGetTrustedArtifactChecksumsWriter(RepositorySystemSession session) { 167 return new SparseDirectoryWriter( 168 getBasedir(session, LOCAL_REPO_PREFIX_DIR, CONFIG_PROP_BASEDIR, true), 169 isOriginAware(session), 170 RepositoryIdHelper.cachedIdToPathSegment(session)); 171 } 172 173 private String calculateArtifactPath( 174 boolean originAware, 175 Artifact artifact, 176 String safeRepositoryId, 177 ChecksumAlgorithmFactory checksumAlgorithmFactory) { 178 String path = localPathComposer.getPathForArtifact(artifact, false) + "." 179 + checksumAlgorithmFactory.getFileExtension(); 180 if (originAware) { 181 path = safeRepositoryId + "/" + path; 182 } 183 return path; 184 } 185 186 private class SparseDirectoryWriter implements Writer { 187 private final Path basedir; 188 189 private final boolean originAware; 190 191 private final Function<ArtifactRepository, String> idToPathSegmentFunction; 192 193 private SparseDirectoryWriter( 194 Path basedir, boolean originAware, Function<ArtifactRepository, String> idToPathSegmentFunction) { 195 this.basedir = basedir; 196 this.originAware = originAware; 197 this.idToPathSegmentFunction = idToPathSegmentFunction; 198 } 199 200 @Override 201 public void addTrustedArtifactChecksums( 202 Artifact artifact, 203 ArtifactRepository artifactRepository, 204 List<ChecksumAlgorithmFactory> checksumAlgorithmFactories, 205 Map<String, String> trustedArtifactChecksums) 206 throws IOException { 207 for (ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories) { 208 Path checksumPath = basedir.resolve(calculateArtifactPath( 209 originAware, 210 artifact, 211 idToPathSegmentFunction.apply(artifactRepository), 212 checksumAlgorithmFactory)); 213 String checksum = requireNonNull(trustedArtifactChecksums.get(checksumAlgorithmFactory.getName())); 214 checksumProcessor.writeChecksum(checksumPath, checksum); 215 } 216 } 217 } 218}