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.FileProcessor;
039import org.slf4j.Logger;
040import org.slf4j.LoggerFactory;
041
042import static java.util.Objects.requireNonNull;
043
044/**
045 * Sparse file {@link FileTrustedChecksumsSourceSupport} implementation that use specified directory as base
046 * directory, where it expects artifacts checksums on standard Maven2 "local" layout. This implementation uses Artifact
047 * coordinates solely to form path from basedir, pretty much as Maven local repository does.
048 * <p>
049 * The source by default is "origin aware", it will factor in origin repository ID as well into base directory name
050 * (for example ".checksums/central/...").
051 * <p>
052 * The checksums files are directly loaded from disk, so in-flight file changes during lifecycle of session are picked
053 * up. This implementation can be simultaneously used to lookup and also write checksums. The written checksums
054 * will become visible across all sessions right after the moment they were written.
055 * <p>
056 * The name of this implementation is "sparseDirectory".
057 *
058 * @see LocalPathComposer
059 * @since 1.9.0
060 */
061@Singleton
062@Named(SparseDirectoryTrustedChecksumsSource.NAME)
063public final class SparseDirectoryTrustedChecksumsSource extends FileTrustedChecksumsSourceSupport {
064    public static final String NAME = "sparseDirectory";
065
066    private static final Logger LOGGER = LoggerFactory.getLogger(SparseDirectoryTrustedChecksumsSource.class);
067
068    private final FileProcessor fileProcessor;
069
070    private final LocalPathComposer localPathComposer;
071
072    @Inject
073    public SparseDirectoryTrustedChecksumsSource(FileProcessor fileProcessor, LocalPathComposer localPathComposer) {
074        super(NAME);
075        this.fileProcessor = requireNonNull(fileProcessor);
076        this.localPathComposer = requireNonNull(localPathComposer);
077    }
078
079    @Override
080    protected Map<String, String> doGetTrustedArtifactChecksums(
081            RepositorySystemSession session,
082            Artifact artifact,
083            ArtifactRepository artifactRepository,
084            List<ChecksumAlgorithmFactory> checksumAlgorithmFactories) {
085        final boolean originAware = isOriginAware(session);
086        final HashMap<String, String> checksums = new HashMap<>();
087        Path basedir = getBasedir(session, false);
088        if (Files.isDirectory(basedir)) {
089            for (ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories) {
090                Path checksumPath = basedir.resolve(
091                        calculateArtifactPath(originAware, artifact, artifactRepository, checksumAlgorithmFactory));
092
093                if (!Files.isRegularFile(checksumPath)) {
094                    LOGGER.debug(
095                            "Artifact '{}' trusted checksum '{}' not found on path '{}'",
096                            artifact,
097                            checksumAlgorithmFactory.getName(),
098                            checksumPath);
099                    continue;
100                }
101
102                try {
103                    String checksum = fileProcessor.readChecksum(checksumPath.toFile());
104                    if (checksum != null) {
105                        checksums.put(checksumAlgorithmFactory.getName(), checksum);
106                    }
107                } catch (IOException e) {
108                    // unexpected, log
109                    LOGGER.warn(
110                            "Could not read artifact '{}' trusted checksum on path '{}'", artifact, checksumPath, e);
111                    throw new UncheckedIOException(e);
112                }
113            }
114        }
115        return checksums;
116    }
117
118    @Override
119    protected SparseDirectoryWriter doGetTrustedArtifactChecksumsWriter(RepositorySystemSession session) {
120        return new SparseDirectoryWriter(getBasedir(session, true), isOriginAware(session));
121    }
122
123    private String calculateArtifactPath(
124            boolean originAware,
125            Artifact artifact,
126            ArtifactRepository artifactRepository,
127            ChecksumAlgorithmFactory checksumAlgorithmFactory) {
128        String path = localPathComposer.getPathForArtifact(artifact, false) + "."
129                + checksumAlgorithmFactory.getFileExtension();
130        if (originAware) {
131            path = artifactRepository.getId() + "/" + path;
132        }
133        return path;
134    }
135
136    private class SparseDirectoryWriter implements Writer {
137        private final Path basedir;
138
139        private final boolean originAware;
140
141        private SparseDirectoryWriter(Path basedir, boolean originAware) {
142            this.basedir = basedir;
143            this.originAware = originAware;
144        }
145
146        @Override
147        public void addTrustedArtifactChecksums(
148                Artifact artifact,
149                ArtifactRepository artifactRepository,
150                List<ChecksumAlgorithmFactory> checksumAlgorithmFactories,
151                Map<String, String> trustedArtifactChecksums)
152                throws IOException {
153            for (ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories) {
154                Path checksumPath = basedir.resolve(
155                        calculateArtifactPath(originAware, artifact, artifactRepository, checksumAlgorithmFactory));
156                String checksum = requireNonNull(trustedArtifactChecksums.get(checksumAlgorithmFactory.getName()));
157                fileProcessor.writeChecksum(checksumPath.toFile(), checksum);
158            }
159        }
160    }
161}