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.List;
025import java.util.Map;
026
027import org.eclipse.aether.ConfigurationProperties;
028import org.eclipse.aether.RepositorySystemSession;
029import org.eclipse.aether.artifact.Artifact;
030import org.eclipse.aether.repository.ArtifactRepository;
031import org.eclipse.aether.repository.RemoteRepository;
032import org.eclipse.aether.spi.checksums.TrustedChecksumsSource;
033import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
034import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory;
035import org.eclipse.aether.util.DirectoryUtils;
036
037import static java.util.Objects.requireNonNull;
038
039/**
040 * Support class for implementing {@link TrustedChecksumsSource} backed by local filesystem. It implements basic support
041 * like basedir calculation, "enabled" flag and "originAware" flag.
042 * <p>
043 * The configuration keys supported:
044 * <ul>
045 *     <li><pre>aether.trustedChecksumsSource.${name}</pre> (boolean) must be explicitly set to "true"
046 *     to become enabled</li>
047 *     <li><pre>aether.trustedChecksumsSource.${name}.basedir</pre> (string, path) directory from where implementation
048 *     can use files. May be relative path (then is resolved against local repository basedir) or absolute. If unset,
049 *     default value is ".checksums" and is resolved against local repository basedir.</li>
050 *     <li><pre>aether.trustedChecksumsSource.${name}.originAware</pre> (boolean) whether to make implementation
051 *     "originAware", to factor in origin repository ID as well or not.</li>
052 * </ul>
053 * <p>
054 * This implementation ensures that implementations have "name" property, used in configuration properties above.
055 *
056 * @since 1.9.0
057 */
058public abstract class FileTrustedChecksumsSourceSupport implements TrustedChecksumsSource {
059    protected static final String CONFIG_PROPS_PREFIX =
060            ConfigurationProperties.PREFIX_AETHER + "trustedChecksumsSource.";
061
062    /**
063     * <b>Experimental:</b> Configuration for "repository key" function.
064     * Note: repository key functions other than "nid" produce repository keys will be <em>way different
065     * that those produced with previous versions or without this option enabled</em>. Checksum source uses this key
066     * function to lay down and look up files to use in sources.
067     *
068     * @since 2.0.14
069     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
070     * @configurationType {@link java.lang.String}
071     * @configurationDefaultValue {@link #DEFAULT_REPOSITORY_KEY_FUNCTION}
072     */
073    public static final String CONFIG_PROP_REPOSITORY_KEY_FUNCTION = CONFIG_PROPS_PREFIX + "repositoryKeyFunction";
074
075    public static final String DEFAULT_REPOSITORY_KEY_FUNCTION = "nid";
076
077    private final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory;
078
079    protected FileTrustedChecksumsSourceSupport(RepositoryKeyFunctionFactory repositoryKeyFunctionFactory) {
080        this.repositoryKeyFunctionFactory = requireNonNull(repositoryKeyFunctionFactory);
081    }
082
083    /**
084     * This implementation will call into underlying code only if enabled, and will enforce non-{@code null} return
085     * value. In worst case, empty map should be returned, meaning "no trusted checksums available".
086     */
087    @Override
088    public Map<String, String> getTrustedArtifactChecksums(
089            RepositorySystemSession session,
090            Artifact artifact,
091            ArtifactRepository artifactRepository,
092            List<ChecksumAlgorithmFactory> checksumAlgorithmFactories) {
093        requireNonNull(session, "session is null");
094        requireNonNull(artifact, "artifact is null");
095        requireNonNull(artifactRepository, "artifactRepository is null");
096        requireNonNull(checksumAlgorithmFactories, "checksumAlgorithmFactories is null");
097        if (isEnabled(session)) {
098            return requireNonNull(
099                    doGetTrustedArtifactChecksums(session, artifact, artifactRepository, checksumAlgorithmFactories));
100        }
101        return null;
102    }
103
104    /**
105     * This implementation will call into underlying code only if enabled. Underlying implementation may still choose
106     * to return {@code null}.
107     */
108    @Override
109    public Writer getTrustedArtifactChecksumsWriter(RepositorySystemSession session) {
110        requireNonNull(session, "session is null");
111        if (isEnabled(session)) {
112            return doGetTrustedArtifactChecksumsWriter(session);
113        }
114        return null;
115    }
116
117    /**
118     * Implementors MUST NOT return {@code null} at this point, as this source is enabled.
119     */
120    protected abstract Map<String, String> doGetTrustedArtifactChecksums(
121            RepositorySystemSession session,
122            Artifact artifact,
123            ArtifactRepository artifactRepository,
124            List<ChecksumAlgorithmFactory> checksumAlgorithmFactories);
125
126    /**
127     * Implementors may override this method and return {@link Writer} instance.
128     */
129    protected Writer doGetTrustedArtifactChecksumsWriter(RepositorySystemSession session) {
130        return null;
131    }
132
133    /**
134     * Returns {@code true} if session configuration marks this instance as enabled.
135     * <p>
136     * Default value is {@code false}.
137     */
138    protected abstract boolean isEnabled(RepositorySystemSession session);
139
140    /**
141     * Uses utility {@link DirectoryUtils#resolveDirectory(RepositorySystemSession, String, String, boolean)} to
142     * calculate (and maybe create) basedir for this implementation, never returns {@code null}. The returned
143     * {@link Path} may not exist, if invoked with {@code mayCreate} being {@code false}.
144     * <p>
145     * Default value is {@code ${LOCAL_REPOSITORY}/.checksums}.
146     *
147     * @return The {@link Path} of basedir, never {@code null}.
148     */
149    protected Path getBasedir(
150            RepositorySystemSession session, String defaultValue, String configPropKey, boolean mayCreate) {
151        try {
152            return DirectoryUtils.resolveDirectory(session, defaultValue, configPropKey, mayCreate);
153        } catch (IOException e) {
154            throw new UncheckedIOException(e);
155        }
156    }
157
158    /**
159     * Returns repository key to be used on file system layout.
160     *
161     * @since 2.0.14
162     */
163    protected String repositoryKey(RepositorySystemSession session, ArtifactRepository artifactRepository) {
164        if (artifactRepository instanceof RemoteRepository) {
165            return repositoryKeyFunctionFactory
166                    .repositoryKeyFunction(
167                            FileTrustedChecksumsSourceSupport.class,
168                            session,
169                            DEFAULT_REPOSITORY_KEY_FUNCTION,
170                            CONFIG_PROP_REPOSITORY_KEY_FUNCTION)
171                    .apply((RemoteRepository) artifactRepository, null);
172        } else {
173            return artifactRepository.getId();
174        }
175    }
176}