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