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("maven2")
53  public final class Maven2RepositoryLayoutFactory implements RepositoryLayoutFactory {
54  
55      public static final String CONFIG_PROP_CHECKSUMS_ALGORITHMS = "aether.checksums.algorithms";
56  
57      private static final String DEFAULT_CHECKSUMS_ALGORITHMS = "SHA-1,MD5";
58  
59      public static final String CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS =
60              "aether.checksums.omitChecksumsForExtensions";
61  
62      private static final String DEFAULT_OMIT_CHECKSUMS_FOR_EXTENSIONS = ".asc,.sigstore";
63  
64      private float priority;
65  
66      private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector;
67  
68      public float getPriority() {
69          return priority;
70      }
71  
72      /**
73       * Service locator ctor.
74       */
75      @Deprecated
76      public Maven2RepositoryLayoutFactory() {
77          this(new DefaultChecksumAlgorithmFactorySelector());
78      }
79  
80      @Inject
81      public Maven2RepositoryLayoutFactory(ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector) {
82          this.checksumAlgorithmFactorySelector = requireNonNull(checksumAlgorithmFactorySelector);
83      }
84  
85      /**
86       * Sets the priority of this component.
87       *
88       * @param priority The priority.
89       * @return This component for chaining, never {@code null}.
90       */
91      public Maven2RepositoryLayoutFactory setPriority(float priority) {
92          this.priority = priority;
93          return this;
94      }
95  
96      public RepositoryLayout newInstance(RepositorySystemSession session, RemoteRepository repository)
97              throws NoRepositoryLayoutException {
98          requireNonNull(session, "session cannot be null");
99          requireNonNull(repository, "repository cannot be null");
100         if (!"default".equals(repository.getContentType())) {
101             throw new NoRepositoryLayoutException(repository);
102         }
103 
104         List<ChecksumAlgorithmFactory> checksumsAlgorithms = checksumAlgorithmFactorySelector.selectList(
105                 ConfigUtils.parseCommaSeparatedUniqueNames(ConfigUtils.getString(
106                         session,
107                         DEFAULT_CHECKSUMS_ALGORITHMS,
108                         CONFIG_PROP_CHECKSUMS_ALGORITHMS + "." + repository.getId(),
109                         CONFIG_PROP_CHECKSUMS_ALGORITHMS)));
110 
111         // ensure uniqueness of (potentially user set) extension list
112         Set<String> omitChecksumsForExtensions = Arrays.stream(ConfigUtils.getString(
113                                 session,
114                                 DEFAULT_OMIT_CHECKSUMS_FOR_EXTENSIONS,
115                                 CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS)
116                         .split(","))
117                 .filter(s -> s != null && !s.trim().isEmpty())
118                 .collect(Collectors.toSet());
119 
120         // validation: enforce that all strings in this set are having leading dot
121         if (omitChecksumsForExtensions.stream().anyMatch(s -> !s.startsWith("."))) {
122             throw new IllegalArgumentException(String.format(
123                     "The configuration %s contains illegal values: %s (all entries must start with '.' (dot))",
124                     CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS, omitChecksumsForExtensions));
125         }
126 
127         return new Maven2RepositoryLayout(
128                 checksumAlgorithmFactorySelector, checksumsAlgorithms, omitChecksumsForExtensions);
129     }
130 
131     private static class Maven2RepositoryLayout implements RepositoryLayout {
132         private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector;
133 
134         private final List<ChecksumAlgorithmFactory> configuredChecksumAlgorithms;
135 
136         private final Set<String> extensionsWithoutChecksums;
137 
138         private Maven2RepositoryLayout(
139                 ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector,
140                 List<ChecksumAlgorithmFactory> configuredChecksumAlgorithms,
141                 Set<String> extensionsWithoutChecksums) {
142             this.checksumAlgorithmFactorySelector = requireNonNull(checksumAlgorithmFactorySelector);
143             this.configuredChecksumAlgorithms = Collections.unmodifiableList(configuredChecksumAlgorithms);
144             this.extensionsWithoutChecksums = requireNonNull(extensionsWithoutChecksums);
145         }
146 
147         private URI toUri(String path) {
148             try {
149                 return new URI(null, null, path, null);
150             } catch (URISyntaxException e) {
151                 throw new IllegalStateException(e);
152             }
153         }
154 
155         @Override
156         public List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories() {
157             return configuredChecksumAlgorithms;
158         }
159 
160         @Override
161         public boolean hasChecksums(Artifact artifact) {
162             String artifactExtension = artifact.getExtension(); // ie. pom.asc
163             for (String extensionWithoutChecksums : extensionsWithoutChecksums) {
164                 if (artifactExtension.endsWith(extensionWithoutChecksums)) {
165                     return false;
166                 }
167             }
168             return true;
169         }
170 
171         @Override
172         public URI getLocation(Artifact artifact, boolean upload) {
173             StringBuilder path = new StringBuilder(128);
174 
175             path.append(artifact.getGroupId().replace('.', '/')).append('/');
176 
177             path.append(artifact.getArtifactId()).append('/');
178 
179             path.append(artifact.getBaseVersion()).append('/');
180 
181             path.append(artifact.getArtifactId()).append('-').append(artifact.getVersion());
182 
183             if (artifact.getClassifier().length() > 0) {
184                 path.append('-').append(artifact.getClassifier());
185             }
186 
187             if (artifact.getExtension().length() > 0) {
188                 path.append('.').append(artifact.getExtension());
189             }
190 
191             return toUri(path.toString());
192         }
193 
194         @Override
195         public URI getLocation(Metadata metadata, boolean upload) {
196             StringBuilder path = new StringBuilder(128);
197 
198             if (metadata.getGroupId().length() > 0) {
199                 path.append(metadata.getGroupId().replace('.', '/')).append('/');
200 
201                 if (metadata.getArtifactId().length() > 0) {
202                     path.append(metadata.getArtifactId()).append('/');
203 
204                     if (metadata.getVersion().length() > 0) {
205                         path.append(metadata.getVersion()).append('/');
206                     }
207                 }
208             }
209 
210             path.append(metadata.getType());
211 
212             return toUri(path.toString());
213         }
214 
215         @Override
216         public List<ChecksumLocation> getChecksumLocations(Artifact artifact, boolean upload, URI location) {
217             if (!hasChecksums(artifact) || isChecksum(artifact.getExtension())) {
218                 return Collections.emptyList();
219             }
220             return getChecksumLocations(location);
221         }
222 
223         @Override
224         public List<ChecksumLocation> getChecksumLocations(Metadata metadata, boolean upload, URI location) {
225             return getChecksumLocations(location);
226         }
227 
228         private List<ChecksumLocation> getChecksumLocations(URI location) {
229             List<ChecksumLocation> checksumLocations = new ArrayList<>(configuredChecksumAlgorithms.size());
230             for (ChecksumAlgorithmFactory checksumAlgorithmFactory : configuredChecksumAlgorithms) {
231                 checksumLocations.add(ChecksumLocation.forLocation(location, checksumAlgorithmFactory));
232             }
233             return checksumLocations;
234         }
235 
236         private boolean isChecksum(String extension) {
237             return checksumAlgorithmFactorySelector.isChecksumExtension(extension);
238         }
239     }
240 }