View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.internal.impl;
20  
21  import javax.inject.Inject;
22  import javax.inject.Named;
23  import javax.inject.Singleton;
24  
25  import java.net.URI;
26  import java.net.URISyntaxException;
27  import java.util.ArrayList;
28  import java.util.Arrays;
29  import java.util.Collections;
30  import java.util.List;
31  import java.util.Set;
32  import java.util.stream.Collectors;
33  
34  import org.eclipse.aether.RepositorySystemSession;
35  import org.eclipse.aether.artifact.Artifact;
36  import org.eclipse.aether.internal.impl.checksum.DefaultChecksumAlgorithmFactorySelector;
37  import org.eclipse.aether.metadata.Metadata;
38  import org.eclipse.aether.repository.RemoteRepository;
39  import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
40  import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
41  import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
42  import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
43  import org.eclipse.aether.transfer.NoRepositoryLayoutException;
44  import org.eclipse.aether.util.ConfigUtils;
45  
46  import static java.util.Objects.requireNonNull;
47  
48  /**
49   * Provides a Maven-2 repository layout for repositories with content type {@code "default"}.
50   */
51  @Singleton
52  @Named(Maven2RepositoryLayoutFactory.NAME)
53  public final class Maven2RepositoryLayoutFactory implements RepositoryLayoutFactory {
54      public static final String NAME = "maven2";
55  
56      public static final String CONFIG_PROP_CHECKSUMS_ALGORITHMS = "aether.checksums.algorithms";
57  
58      private static final String DEFAULT_CHECKSUMS_ALGORITHMS = "SHA-1,MD5";
59  
60      public static final String CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS =
61              "aether.checksums.omitChecksumsForExtensions";
62  
63      private static final String DEFAULT_OMIT_CHECKSUMS_FOR_EXTENSIONS = ".asc,.sigstore";
64  
65      private float priority;
66  
67      private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector;
68  
69      public float getPriority() {
70          return priority;
71      }
72  
73      /**
74       * Service locator ctor.
75       */
76      @Deprecated
77      public Maven2RepositoryLayoutFactory() {
78          this(new DefaultChecksumAlgorithmFactorySelector());
79      }
80  
81      @Inject
82      public Maven2RepositoryLayoutFactory(ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector) {
83          this.checksumAlgorithmFactorySelector = requireNonNull(checksumAlgorithmFactorySelector);
84      }
85  
86      /**
87       * Sets the priority of this component.
88       *
89       * @param priority The priority.
90       * @return This component for chaining, never {@code null}.
91       */
92      public Maven2RepositoryLayoutFactory setPriority(float priority) {
93          this.priority = priority;
94          return this;
95      }
96  
97      public RepositoryLayout newInstance(RepositorySystemSession session, RemoteRepository repository)
98              throws NoRepositoryLayoutException {
99          requireNonNull(session, "session cannot be null");
100         requireNonNull(repository, "repository cannot be null");
101         if (!"default".equals(repository.getContentType())) {
102             throw new NoRepositoryLayoutException(repository);
103         }
104 
105         List<ChecksumAlgorithmFactory> checksumsAlgorithms = checksumAlgorithmFactorySelector.selectList(
106                 ConfigUtils.parseCommaSeparatedUniqueNames(ConfigUtils.getString(
107                         session,
108                         DEFAULT_CHECKSUMS_ALGORITHMS,
109                         CONFIG_PROP_CHECKSUMS_ALGORITHMS + "." + repository.getId(),
110                         CONFIG_PROP_CHECKSUMS_ALGORITHMS)));
111 
112         // ensure uniqueness of (potentially user set) extension list
113         Set<String> omitChecksumsForExtensions = Arrays.stream(ConfigUtils.getString(
114                                 session,
115                                 DEFAULT_OMIT_CHECKSUMS_FOR_EXTENSIONS,
116                                 CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS)
117                         .split(","))
118                 .filter(s -> s != null && !s.trim().isEmpty())
119                 .collect(Collectors.toSet());
120 
121         // validation: enforce that all strings in this set are having leading dot
122         if (omitChecksumsForExtensions.stream().anyMatch(s -> !s.startsWith("."))) {
123             throw new IllegalArgumentException(String.format(
124                     "The configuration %s contains illegal values: %s (all entries must start with '.' (dot))",
125                     CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS, omitChecksumsForExtensions));
126         }
127 
128         return new Maven2RepositoryLayout(
129                 checksumAlgorithmFactorySelector, checksumsAlgorithms, omitChecksumsForExtensions);
130     }
131 
132     private static class Maven2RepositoryLayout implements RepositoryLayout {
133         private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector;
134 
135         private final List<ChecksumAlgorithmFactory> configuredChecksumAlgorithms;
136 
137         private final Set<String> extensionsWithoutChecksums;
138 
139         private Maven2RepositoryLayout(
140                 ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector,
141                 List<ChecksumAlgorithmFactory> configuredChecksumAlgorithms,
142                 Set<String> extensionsWithoutChecksums) {
143             this.checksumAlgorithmFactorySelector = requireNonNull(checksumAlgorithmFactorySelector);
144             this.configuredChecksumAlgorithms = Collections.unmodifiableList(configuredChecksumAlgorithms);
145             this.extensionsWithoutChecksums = requireNonNull(extensionsWithoutChecksums);
146         }
147 
148         private URI toUri(String path) {
149             try {
150                 return new URI(null, null, path, null);
151             } catch (URISyntaxException e) {
152                 throw new IllegalStateException(e);
153             }
154         }
155 
156         @Override
157         public List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories() {
158             return configuredChecksumAlgorithms;
159         }
160 
161         @Override
162         public boolean hasChecksums(Artifact artifact) {
163             String artifactExtension = artifact.getExtension(); // ie. pom.asc
164             for (String extensionWithoutChecksums : extensionsWithoutChecksums) {
165                 if (artifactExtension.endsWith(extensionWithoutChecksums)) {
166                     return false;
167                 }
168             }
169             return true;
170         }
171 
172         @Override
173         public URI getLocation(Artifact artifact, boolean upload) {
174             StringBuilder path = new StringBuilder(128);
175 
176             path.append(artifact.getGroupId().replace('.', '/')).append('/');
177 
178             path.append(artifact.getArtifactId()).append('/');
179 
180             path.append(artifact.getBaseVersion()).append('/');
181 
182             path.append(artifact.getArtifactId()).append('-').append(artifact.getVersion());
183 
184             if (artifact.getClassifier().length() > 0) {
185                 path.append('-').append(artifact.getClassifier());
186             }
187 
188             if (artifact.getExtension().length() > 0) {
189                 path.append('.').append(artifact.getExtension());
190             }
191 
192             return toUri(path.toString());
193         }
194 
195         @Override
196         public URI getLocation(Metadata metadata, boolean upload) {
197             StringBuilder path = new StringBuilder(128);
198 
199             if (metadata.getGroupId().length() > 0) {
200                 path.append(metadata.getGroupId().replace('.', '/')).append('/');
201 
202                 if (metadata.getArtifactId().length() > 0) {
203                     path.append(metadata.getArtifactId()).append('/');
204 
205                     if (metadata.getVersion().length() > 0) {
206                         path.append(metadata.getVersion()).append('/');
207                     }
208                 }
209             }
210 
211             path.append(metadata.getType());
212 
213             return toUri(path.toString());
214         }
215 
216         @Override
217         public List<ChecksumLocation> getChecksumLocations(Artifact artifact, boolean upload, URI location) {
218             if (!hasChecksums(artifact) || isChecksum(artifact.getExtension())) {
219                 return Collections.emptyList();
220             }
221             return getChecksumLocations(location);
222         }
223 
224         @Override
225         public List<ChecksumLocation> getChecksumLocations(Metadata metadata, boolean upload, URI location) {
226             return getChecksumLocations(location);
227         }
228 
229         private List<ChecksumLocation> getChecksumLocations(URI location) {
230             List<ChecksumLocation> checksumLocations = new ArrayList<>(configuredChecksumAlgorithms.size());
231             for (ChecksumAlgorithmFactory checksumAlgorithmFactory : configuredChecksumAlgorithms) {
232                 checksumLocations.add(ChecksumLocation.forLocation(location, checksumAlgorithmFactory));
233             }
234             return checksumLocations;
235         }
236 
237         private boolean isChecksum(String extension) {
238             return checksumAlgorithmFactorySelector.isChecksumExtension(extension);
239         }
240     }
241 }