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 java.io.IOException;
022import java.io.UncheckedIOException;
023import java.nio.file.Path;
024import java.util.ArrayList;
025import java.util.List;
026import java.util.Map;
027
028import org.eclipse.aether.ConfigurationProperties;
029import org.eclipse.aether.RepositorySystemSession;
030import org.eclipse.aether.artifact.Artifact;
031import org.eclipse.aether.metadata.Metadata;
032import org.eclipse.aether.repository.ArtifactRepository;
033import org.eclipse.aether.repository.RemoteRepository;
034import org.eclipse.aether.spi.checksums.TrustedChecksumsSource;
035import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
036import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory;
037import org.eclipse.aether.util.DirectoryUtils;
038import org.slf4j.Logger;
039import org.slf4j.LoggerFactory;
040
041import static java.util.Objects.requireNonNull;
042
043/**
044 * Support class for implementing {@link TrustedChecksumsSource} backed by local filesystem. It implements basic support
045 * like basedir calculation, "enabled" flag and "originAware" flag.
046 * <p>
047 * The configuration keys supported:
048 * <ul>
049 *     <li><pre>aether.trustedChecksumsSource.${name}</pre> (boolean) must be explicitly set to "true"
050 *     to become enabled</li>
051 *     <li><pre>aether.trustedChecksumsSource.${name}.basedir</pre> (string, path) directory from where implementation
052 *     can use files. May be relative path (then is resolved against local repository basedir) or absolute. If unset,
053 *     default value is ".checksums" and is resolved against local repository basedir.</li>
054 *     <li><pre>aether.trustedChecksumsSource.${name}.originAware</pre> (boolean) whether to make implementation
055 *     "originAware", to factor in origin repository ID as well or not.</li>
056 * </ul>
057 * <p>
058 * This implementation ensures that implementations have "name" property, used in configuration properties above.
059 *
060 * @since 1.9.0
061 */
062public abstract class FileTrustedChecksumsSourceSupport implements TrustedChecksumsSource {
063    protected static final String CONFIG_PROPS_PREFIX =
064            ConfigurationProperties.PREFIX_AETHER + "trustedChecksumsSource.";
065
066    protected final Logger logger = LoggerFactory.getLogger(getClass());
067
068    private final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory;
069
070    protected FileTrustedChecksumsSourceSupport(RepositoryKeyFunctionFactory repositoryKeyFunctionFactory) {
071        this.repositoryKeyFunctionFactory = requireNonNull(repositoryKeyFunctionFactory);
072    }
073
074    /**
075     * This implementation will call into underlying code only if enabled, and will enforce non-{@code null} return
076     * value. In worst case, empty map should be returned, meaning "no trusted checksums available".
077     */
078    @Override
079    public Map<String, String> getTrustedArtifactChecksums(
080            RepositorySystemSession session,
081            Artifact artifact,
082            ArtifactRepository artifactRepository,
083            List<ChecksumAlgorithmFactory> checksumAlgorithmFactories) {
084        requireNonNull(session, "session is null");
085        requireNonNull(artifact, "artifact is null");
086        requireNonNull(artifactRepository, "artifactRepository is null");
087        requireNonNull(checksumAlgorithmFactories, "checksumAlgorithmFactories is null");
088        if (isEnabled(session)) {
089            return requireNonNull(
090                    doGetTrustedArtifactChecksums(session, artifact, artifactRepository, checksumAlgorithmFactories));
091        }
092        return null;
093    }
094
095    /**
096     * This implementation will call into underlying code only if enabled, and will enforce non-{@code null} return
097     * value. In worst case, empty map should be returned, meaning "no trusted checksums available".
098     *
099     * @since 2.0.23
100     */
101    @Override
102    public Map<String, String> getTrustedMetadataChecksums(
103            RepositorySystemSession session,
104            Metadata metadata,
105            ArtifactRepository artifactRepository,
106            List<ChecksumAlgorithmFactory> checksumAlgorithmFactories) {
107        requireNonNull(session, "session is null");
108        requireNonNull(metadata, "metadata is null");
109        requireNonNull(artifactRepository, "artifactRepository is null");
110        requireNonNull(checksumAlgorithmFactories, "checksumAlgorithmFactories is null");
111        if (isEnabled(session)) {
112            return requireNonNull(
113                    doGetTrustedMetadataChecksums(session, metadata, artifactRepository, checksumAlgorithmFactories));
114        }
115        return null;
116    }
117
118    /**
119     * This implementation will call into underlying code only if enabled. Underlying implementation may still choose
120     * to return {@code null}.
121     */
122    @Override
123    public Writer getTrustedArtifactChecksumsWriter(RepositorySystemSession session) {
124        requireNonNull(session, "session is null");
125        if (isEnabled(session)) {
126            return doGetTrustedArtifactChecksumsWriter(session);
127        }
128        return null;
129    }
130
131    /**
132     * Implementors MUST NOT return {@code null} at this point, as this source is enabled.
133     */
134    protected abstract Map<String, String> doGetTrustedArtifactChecksums(
135            RepositorySystemSession session,
136            Artifact artifact,
137            ArtifactRepository artifactRepository,
138            List<ChecksumAlgorithmFactory> checksumAlgorithmFactories);
139
140    /**
141     * Implementors MUST NOT return {@code null} at this point, as this source is enabled. Metadata checksums are
142     * looked up using the same file conventions as artifact checksums, with the metadata path composed from the
143     * origin repository key (for example {@code g/a/v/maven-metadata-central.xml}).
144     *
145     * @since 2.0.23
146     */
147    protected abstract Map<String, String> doGetTrustedMetadataChecksums(
148            RepositorySystemSession session,
149            Metadata metadata,
150            ArtifactRepository artifactRepository,
151            List<ChecksumAlgorithmFactory> checksumAlgorithmFactories);
152
153    /**
154     * Implementors may override this method and return {@link Writer} instance.
155     */
156    protected Writer doGetTrustedArtifactChecksumsWriter(RepositorySystemSession session) {
157        return null;
158    }
159
160    /**
161     * Returns {@code true} if session configuration marks this instance as enabled.
162     * <p>
163     * Default value is {@code false}.
164     */
165    protected abstract boolean isEnabled(RepositorySystemSession session);
166
167    /**
168     * Uses utility {@link DirectoryUtils#resolveDirectory(RepositorySystemSession, String, String, boolean)} to
169     * calculate (and maybe create) basedir for this implementation, never returns {@code null}. The returned
170     * {@link Path} may not exist, if invoked with {@code mayCreate} being {@code false}.
171     * <p>
172     * Default value is {@code ${LOCAL_REPOSITORY}/.checksums}.
173     *
174     * @return The {@link Path} of basedir, never {@code null}.
175     */
176    protected Path getBasedir(
177            RepositorySystemSession session, String defaultValue, String configPropKey, boolean mayCreate) {
178        try {
179            return DirectoryUtils.resolveDirectory(session, defaultValue, configPropKey, mayCreate);
180        } catch (IOException e) {
181            throw new UncheckedIOException(e);
182        }
183    }
184
185    /**
186     * Returns repository keys to be used on file system layout. Always returns a list with at least one element.
187     * Elements are sorted from "most specific" to "least specific" keys.
188     *
189     * @since 2.0.14
190     */
191    protected List<String> repositoryKey(RepositorySystemSession session, ArtifactRepository artifactRepository) {
192        ArrayList<String> keys = new ArrayList<>();
193        if (artifactRepository instanceof RemoteRepository) {
194            RemoteRepository rr = (RemoteRepository) artifactRepository;
195            keys.add(repositoryKeyFunctionFactory
196                    .trackingRepositoryKeyFunction(session)
197                    .apply(rr, null));
198            keys.add(repositoryKeyFunctionFactory
199                    .systemRepositoryKeyFunction(session)
200                    .apply(rr, null));
201        } else {
202            keys.add(artifactRepository.getId());
203        }
204        return keys;
205    }
206}