001package org.eclipse.aether.internal.impl.checksum;
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 javax.inject.Inject;
023import javax.inject.Named;
024import javax.inject.Singleton;
025
026import java.io.IOException;
027import java.io.UncheckedIOException;
028import java.nio.file.Files;
029import java.nio.file.Path;
030import java.util.HashMap;
031import java.util.List;
032import java.util.Map;
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.FileProcessor;
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
065        extends FileTrustedChecksumsSourceSupport
066{
067    public static final String NAME = "sparseDirectory";
068
069    private static final Logger LOGGER = LoggerFactory.getLogger( SparseDirectoryTrustedChecksumsSource.class );
070
071    private final FileProcessor fileProcessor;
072
073    private final LocalPathComposer localPathComposer;
074
075    @Inject
076    public SparseDirectoryTrustedChecksumsSource( FileProcessor fileProcessor, LocalPathComposer localPathComposer )
077    {
078        super( NAME );
079        this.fileProcessor = requireNonNull( fileProcessor );
080        this.localPathComposer = requireNonNull( localPathComposer );
081    }
082
083    @Override
084    protected Map<String, String> doGetTrustedArtifactChecksums(
085            RepositorySystemSession session, Artifact artifact, ArtifactRepository artifactRepository,
086            List<ChecksumAlgorithmFactory> checksumAlgorithmFactories )
087    {
088        final boolean originAware = isOriginAware( session );
089        final HashMap<String, String> checksums = new HashMap<>();
090        Path basedir = getBasedir( session, false );
091        if ( Files.isDirectory( basedir ) )
092        {
093            for ( ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories )
094            {
095                Path checksumPath = basedir.resolve(
096                        calculateArtifactPath( originAware, artifact, artifactRepository, checksumAlgorithmFactory ) );
097
098                if ( !Files.isRegularFile( checksumPath ) )
099                {
100                    LOGGER.debug( "Artifact '{}' trusted checksum '{}' not found on path '{}'",
101                            artifact, checksumAlgorithmFactory.getName(), checksumPath );
102                    continue;
103                }
104
105                try
106                {
107                    String checksum = fileProcessor.readChecksum( checksumPath.toFile() );
108                    if ( checksum != null )
109                    {
110                        checksums.put( checksumAlgorithmFactory.getName(), checksum );
111                    }
112                }
113                catch ( IOException e )
114                {
115                    // unexpected, log
116                    LOGGER.warn( "Could not read artifact '{}' trusted checksum on path '{}'", artifact, checksumPath,
117                            e );
118                    throw new UncheckedIOException( e );
119                }
120            }
121        }
122        return checksums;
123    }
124
125    @Override
126    protected SparseDirectoryWriter doGetTrustedArtifactChecksumsWriter( RepositorySystemSession session )
127    {
128        return new SparseDirectoryWriter( getBasedir( session, true ), isOriginAware( session ) );
129    }
130
131    private String calculateArtifactPath( boolean originAware,
132                                          Artifact artifact,
133                                          ArtifactRepository artifactRepository,
134                                          ChecksumAlgorithmFactory checksumAlgorithmFactory )
135    {
136        String path = localPathComposer.getPathForArtifact( artifact, false )
137                + "." + checksumAlgorithmFactory.getFileExtension();
138        if ( originAware )
139        {
140            path = artifactRepository.getId() + "/" + path;
141        }
142        return path;
143    }
144
145    private class SparseDirectoryWriter implements Writer
146    {
147        private final Path basedir;
148
149        private final boolean originAware;
150
151        private SparseDirectoryWriter( Path basedir, boolean originAware )
152        {
153            this.basedir = basedir;
154            this.originAware = originAware;
155        }
156
157        @Override
158        public void addTrustedArtifactChecksums( Artifact artifact,
159                                                 ArtifactRepository artifactRepository,
160                                                 List<ChecksumAlgorithmFactory> checksumAlgorithmFactories,
161                                                 Map<String, String> trustedArtifactChecksums ) throws IOException
162        {
163            for ( ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithmFactories )
164            {
165                Path checksumPath = basedir.resolve( calculateArtifactPath(
166                        originAware, artifact, artifactRepository, checksumAlgorithmFactory ) );
167                String checksum = requireNonNull(
168                        trustedArtifactChecksums.get( checksumAlgorithmFactory.getName() ) );
169                fileProcessor.writeChecksum( checksumPath.toFile(), checksum );
170            }
171        }
172   }
173}