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.spi.connector.layout;
020
021import java.net.URI;
022import java.util.List;
023
024import org.eclipse.aether.artifact.Artifact;
025import org.eclipse.aether.metadata.Metadata;
026import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
027
028import static java.util.Objects.requireNonNull;
029
030/**
031 * The layout for a remote repository whose artifacts/metadata can be addressed via URIs.
032 * <p>
033 * <strong>Note:</strong> Implementations must be stateless.
034 */
035public interface RepositoryLayout {
036
037    /**
038     * A descriptor for a checksum location. This descriptor simply associates the location of a checksum file with the
039     * underlying checksum algorithm used to calculate/verify it.
040     */
041    final class ChecksumLocation {
042        private final URI location;
043
044        private final ChecksumAlgorithmFactory checksumAlgorithmFactory;
045
046        /**
047         * Creates a new checksum file descriptor with the specified algorithm and location. The method
048         * {@link #forLocation(URI, ChecksumAlgorithmFactory)} is usually more convenient though.
049         *
050         * @param location                 The relative URI to the checksum file within a repository, must not be {@code
051         *                                 null}.
052         * @param checksumAlgorithmFactory The checksum type used to calculate the checksum, must not be {@code null}.
053         */
054        public ChecksumLocation(URI location, ChecksumAlgorithmFactory checksumAlgorithmFactory) {
055            verify(location, checksumAlgorithmFactory);
056            this.location = location;
057            this.checksumAlgorithmFactory = checksumAlgorithmFactory;
058        }
059
060        /**
061         * Creates a checksum descriptor for the specified artifact/metadata location and algorithm. The location
062         * of the checksum file itself is derived from the supplied resource URI by appending the file extension
063         * specified by the algorithm factory. See {@link ChecksumAlgorithmFactory#getFileExtension()}.
064         *
065         * @param location                 The relative URI to the artifact/metadata whose checksum file is being
066         *                                 obtained, must not be
067         *                                 {@code null} and must not have a query or fragment part.
068         * @param checksumAlgorithmFactory The algorithm used to calculate the checksum, must not be {@code null}.
069         * @return The checksum file descriptor, never {@code null}.
070         */
071        public static ChecksumLocation forLocation(URI location, ChecksumAlgorithmFactory checksumAlgorithmFactory) {
072            verify(location, checksumAlgorithmFactory);
073            if (location.getRawQuery() != null) {
074                throw new IllegalArgumentException("resource location must not have query parameters: " + location);
075            }
076            if (location.getRawFragment() != null) {
077                throw new IllegalArgumentException("resource location must not have a fragment: " + location);
078            }
079            return new ChecksumLocation(
080                    URI.create(location + "." + checksumAlgorithmFactory.getFileExtension()), checksumAlgorithmFactory);
081        }
082
083        private static void verify(URI location, ChecksumAlgorithmFactory checksumAlgorithmFactory) {
084            requireNonNull(location, "checksum location cannot be null");
085            if (location.isAbsolute()) {
086                throw new IllegalArgumentException("checksum location must be relative");
087            }
088            requireNonNull(checksumAlgorithmFactory, "checksum algorithm factory cannot be null");
089        }
090
091        /**
092         * Gets the {@link ChecksumAlgorithmFactory} that is used to calculate the checksum.
093         *
094         * @return The checksum factory, never {@code null}.
095         */
096        public ChecksumAlgorithmFactory getChecksumAlgorithmFactory() {
097            return checksumAlgorithmFactory;
098        }
099
100        /**
101         * Gets the location of the checksum file with a remote repository. The URI is relative to the root directory of
102         * the repository.
103         *
104         * @return The relative URI to the checksum file, never {@code null}.
105         */
106        public URI getLocation() {
107            return location;
108        }
109
110        @Override
111        public String toString() {
112            return location + " (" + checksumAlgorithmFactory.getName() + ")";
113        }
114    }
115
116    /**
117     * Returns immutable list of {@link ChecksumAlgorithmFactory} this instance of layout uses, never {@code null}.
118     * This (legacy, but not deprecated) method will return <em>all checksums this layout uses</em>, but
119     * these may be different in case upload or download checksums are explicitly configured. This method will
120     * reflect the checksum order used for download, but may have more elements than actually used in download
121     * validation, if the generated checksums for upload has extra elements.
122     *
123     * @see org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind
124     * @see #getChecksumAlgorithmFactories(boolean)
125     * @since 1.8.0
126     */
127    List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories();
128
129    /**
130     * Returns immutable list of {@link ChecksumAlgorithmFactory} this instance of layout uses for download or upload,
131     * never {@code null}. The order also represents the order how remote external checksums are retrieved and
132     * validated (if for download).
133     *
134     * @param upload {@code false} if the caller needs checksums used for download validation, or {@code true} if the
135     *                            caller needs checksums generated for upload.
136     * @see org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind
137     * @since 2.0.15
138     */
139    default List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories(boolean upload) {
140        return getChecksumAlgorithmFactories();
141    }
142
143    /**
144     * Tells whether given artifact have remote external checksums according to current layout or not. If it returns
145     * {@code true}, then layout configured checksums will be expected: on upload they will be calculated and deployed
146     * along artifact, on download they will be retrieved and validated.
147     *
148     * If it returns {@code false} the given artifacts will have checksums omitted: on upload they will not be
149     * calculated and deployed, and on download they will be not retrieved nor validated.
150     *
151     * The result affects only layout provided checksums. See
152     * {@link org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind#REMOTE_EXTERNAL}.
153     * On download, the {@link org.eclipse.aether.spi.connector.layout.RepositoryLayout#getChecksumAlgorithmFactories(boolean)}
154     * layout required checksums are calculated, and non layout-provided checksums are still utilized.
155     *
156     * Typical case to return {@code false} (to omit checksums) is for artifact signatures, that are already a
157     * "sub-artifact" of some main artifact (for example a JAR), and they can be validated by some other means.
158     *
159     * @see org.eclipse.aether.spi.artifact.ArtifactPredicate#isWithoutChecksum(Artifact)
160     * @since 1.8.0
161     */
162    boolean hasChecksums(Artifact artifact);
163
164    /**
165     * Gets the location within a remote repository where the specified artifact resides. The URI is relative to the
166     * root directory of the repository.
167     *
168     * @param artifact The artifact to get the URI for, must not be {@code null}.
169     * @param upload   {@code false} if the artifact is being downloaded, {@code true} if the artifact is being
170     *                 uploaded.
171     * @return The relative URI to the artifact, never {@code null}.
172     */
173    URI getLocation(Artifact artifact, boolean upload);
174
175    /**
176     * Gets the location within a remote repository where the specified metadata resides. The URI is relative to the
177     * root directory of the repository.
178     *
179     * @param metadata The metadata to get the URI for, must not be {@code null}.
180     * @param upload   {@code false} if the metadata is being downloaded, {@code true} if the metadata is being
181     *                 uploaded.
182     * @return The relative URI to the metadata, never {@code null}.
183     */
184    URI getLocation(Metadata metadata, boolean upload);
185
186    /**
187     * Gets the checksums files that a remote repository keeps to help detect data corruption during transfers of the
188     * specified artifact.
189     *
190     * @param artifact The artifact to get the checksum files for, must not be {@code null}.
191     * @param upload   {@code false} if the checksums are being downloaded/verified, {@code true} if the checksums are
192     *                 being uploaded/created.
193     * @param location The relative URI to the artifact within the repository as previously obtained from
194     *                 {@link #getLocation(Artifact, boolean)}, must not be {@code null}.
195     * @return The checksum files for the given artifact, possibly empty but never {@code null}. If empty, that means
196     * that this layout does not provide checksums for given artifact.
197     */
198    List<ChecksumLocation> getChecksumLocations(Artifact artifact, boolean upload, URI location);
199
200    /**
201     * Gets the checksums files that a remote repository keeps to help detect data corruption during transfers of the
202     * specified metadata.
203     *
204     * @param metadata The metadata to get the checksum files for, must not be {@code null}.
205     * @param upload   {@code false} if the checksums are being downloaded/verified, {@code true} if the checksums are
206     *                 being uploaded/created.
207     * @param location The relative URI to the metadata within the repository as previously obtained from
208     *                 {@link #getLocation(Metadata, boolean)}, must not be {@code null}.
209     * @return The checksum files for the given metadata, possibly empty but never {@code null}. If empty, that means
210     * that this layout does not provide checksums for given artifact.
211     */
212    List<ChecksumLocation> getChecksumLocations(Metadata metadata, boolean upload, URI location);
213}