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