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.filter;
020
021import java.io.IOException;
022import java.io.UncheckedIOException;
023import java.nio.file.Path;
024
025import org.eclipse.aether.ConfigurationProperties;
026import org.eclipse.aether.RepositorySystemSession;
027import org.eclipse.aether.internal.impl.checksum.FileTrustedChecksumsSourceSupport;
028import org.eclipse.aether.repository.RemoteRepository;
029import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilter;
030import org.eclipse.aether.spi.connector.filter.RemoteRepositoryFilterSource;
031import org.eclipse.aether.spi.remoterepo.RepositoryKeyFunctionFactory;
032import org.eclipse.aether.util.DirectoryUtils;
033
034import static java.util.Objects.requireNonNull;
035
036/**
037 * Support class for {@link RemoteRepositoryFilterSource} implementations.
038 * <p>
039 * Support class for implementing {@link RemoteRepositoryFilterSource}. It implements basic support
040 * like optional "basedir" calculation, handling of "enabled" flag.
041 * <p>
042 * The configuration keys supported:
043 * <ul>
044 *     <li><pre>aether.remoteRepositoryFilter.${id}.enabled</pre> (boolean) must be explicitly set to "true"
045 *     to become enabled</li>
046 *     <li><pre>aether.remoteRepositoryFilter.${id}.basedir</pre> (string, path) directory from where implementation
047 *     can use files. If unset, default value is ".remoteRepositoryFilters/${id}" and is resolved from local
048 *     repository basedir.</li>
049 * </ul>
050 *
051 * @since 1.9.0
052 */
053public abstract class RemoteRepositoryFilterSourceSupport implements RemoteRepositoryFilterSource {
054    protected static final String CONFIG_PROPS_PREFIX =
055            ConfigurationProperties.PREFIX_AETHER + "remoteRepositoryFilter.";
056
057    /**
058     * <b>Experimental:</b> Configuration for "repository key" function.
059     * Note: repository key functions other than "nid" produce repository keys will be <em>way different
060     * that those produced with previous versions or without this option enabled</em>. Filter uses this key function to
061     * lay down and look up files to use in filtering.
062     *
063     * @since 2.0.14
064     * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
065     * @configurationType {@link java.lang.String}
066     * @configurationDefaultValue {@link #DEFAULT_REPOSITORY_KEY_FUNCTION}
067     */
068    public static final String CONFIG_PROP_REPOSITORY_KEY_FUNCTION = CONFIG_PROPS_PREFIX + "repositoryKeyFunction";
069
070    public static final String DEFAULT_REPOSITORY_KEY_FUNCTION = "nid";
071
072    private final RepositoryKeyFunctionFactory repositoryKeyFunctionFactory;
073
074    protected RemoteRepositoryFilterSourceSupport(RepositoryKeyFunctionFactory repositoryKeyFunctionFactory) {
075        this.repositoryKeyFunctionFactory = requireNonNull(repositoryKeyFunctionFactory);
076    }
077
078    /**
079     * Returns {@code true} if session configuration contains this name set to {@code true}.
080     * <p>
081     * Default is {@code true}.
082     */
083    protected abstract boolean isEnabled(RepositorySystemSession session);
084
085    /**
086     * Uses common {@link DirectoryUtils#resolveDirectory(RepositorySystemSession, String, String, boolean)} to
087     * calculate (and maybe create) basedir for this implementation, never returns {@code null}. The returned
088     * {@link Path} may not exists, if invoked with {@code mayCreate} being {@code false}.
089     * <p>
090     * Default value is {@code ${LOCAL_REPOSITORY}/.checksums}.
091     *
092     * @return The {@link Path} of basedir, never {@code null}.
093     */
094    protected Path getBasedir(
095            RepositorySystemSession session, String defaultValue, String configPropKey, boolean mayCreate) {
096        try {
097            return DirectoryUtils.resolveDirectory(session, defaultValue, configPropKey, mayCreate);
098        } catch (IOException e) {
099            throw new UncheckedIOException(e);
100        }
101    }
102
103    /**
104     * We use remote repositories as keys, so normalize them.
105     *
106     * @since 2.0.14
107     * @see RemoteRepository#toBareRemoteRepository()
108     */
109    protected RemoteRepository normalizeRemoteRepository(
110            RepositorySystemSession session, RemoteRepository remoteRepository) {
111        return remoteRepository.toBareRemoteRepository();
112    }
113
114    /**
115     * Returns repository key to be used on file system layout.
116     *
117     * @since 2.0.14
118     */
119    protected String repositoryKey(RepositorySystemSession session, RemoteRepository repository) {
120        return repositoryKeyFunctionFactory
121                .repositoryKeyFunction(
122                        FileTrustedChecksumsSourceSupport.class,
123                        session,
124                        DEFAULT_REPOSITORY_KEY_FUNCTION,
125                        CONFIG_PROP_REPOSITORY_KEY_FUNCTION)
126                .apply(repository, null);
127    }
128
129    /**
130     * Simple {@link RemoteRepositoryFilter.Result} immutable implementation.
131     */
132    private static class SimpleResult implements RemoteRepositoryFilter.Result {
133        private final boolean accepted;
134
135        private final String reasoning;
136
137        private SimpleResult(boolean accepted, String reasoning) {
138            this.accepted = accepted;
139            this.reasoning = requireNonNull(reasoning);
140        }
141
142        @Override
143        public boolean isAccepted() {
144            return accepted;
145        }
146
147        @Override
148        public String reasoning() {
149            return reasoning;
150        }
151    }
152
153    /**
154     * Visible for testing.
155     */
156    static RemoteRepositoryFilter.Result result(boolean accepted, String name, String message) {
157        return new SimpleResult(accepted, name + ": " + message);
158    }
159}