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.Collections;
29  import java.util.List;
30  
31  import org.eclipse.aether.ConfigurationProperties;
32  import org.eclipse.aether.RepositorySystemSession;
33  import org.eclipse.aether.artifact.Artifact;
34  import org.eclipse.aether.metadata.Metadata;
35  import org.eclipse.aether.repository.RemoteRepository;
36  import org.eclipse.aether.spi.artifact.ArtifactPredicate;
37  import org.eclipse.aether.spi.artifact.ArtifactPredicateFactory;
38  import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
39  import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
40  import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
41  import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
42  import org.eclipse.aether.transfer.NoRepositoryLayoutException;
43  import org.eclipse.aether.util.ConfigUtils;
44  
45  import static java.util.Objects.requireNonNull;
46  
47  /**
48   * Provides a Maven-2 repository layout for repositories with content type {@code "default"}.
49   */
50  @Singleton
51  @Named(Maven2RepositoryLayoutFactory.NAME)
52  public final class Maven2RepositoryLayoutFactory implements RepositoryLayoutFactory {
53      public static final String NAME = "maven2";
54  
55      private static final String CONFIG_PROPS_PREFIX = ConfigurationProperties.PREFIX_LAYOUT + NAME + ".";
56  
57      /**
58       * Comma-separated list of checksum algorithms with which checksums are validated (downloaded) and generated
59       * (uploaded) with this layout. Resolver by default supports following algorithms: MD5, SHA-1, SHA-256 and
60       * SHA-512. New algorithms can be added by implementing ChecksumAlgorithmFactory component.
61       *
62       * @since 1.8.0
63       * @configurationSource {@link RepositorySystemSession#getConfigProperties()}
64       * @configurationType {@link java.lang.String}
65       * @configurationDefaultValue {@link #DEFAULT_CHECKSUMS_ALGORITHMS}
66       * @configurationRepoIdSuffix Yes
67       */
68      public static final String CONFIG_PROP_CHECKSUMS_ALGORITHMS = CONFIG_PROPS_PREFIX + "checksumAlgorithms";
69  
70      public static final String DEFAULT_CHECKSUMS_ALGORITHMS = "SHA-1,MD5";
71  
72      private float priority;
73  
74      private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector;
75  
76      private final ArtifactPredicateFactory artifactPredicateFactory;
77  
78      public float getPriority() {
79          return priority;
80      }
81  
82      @Inject
83      public Maven2RepositoryLayoutFactory(
84              ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector,
85              ArtifactPredicateFactory artifactPredicateFactory) {
86          this.checksumAlgorithmFactorySelector = requireNonNull(checksumAlgorithmFactorySelector);
87          this.artifactPredicateFactory = requireNonNull(artifactPredicateFactory);
88      }
89  
90      /**
91       * Sets the priority of this component.
92       *
93       * @param priority The priority.
94       * @return This component for chaining, never {@code null}.
95       */
96      public Maven2RepositoryLayoutFactory setPriority(float priority) {
97          this.priority = priority;
98          return this;
99      }
100 
101     public RepositoryLayout newInstance(RepositorySystemSession session, RemoteRepository repository)
102             throws NoRepositoryLayoutException {
103         requireNonNull(session, "session cannot be null");
104         requireNonNull(repository, "repository cannot be null");
105         if (!"default".equals(repository.getContentType())) {
106             throw new NoRepositoryLayoutException(repository);
107         }
108 
109         List<ChecksumAlgorithmFactory> checksumsAlgorithms = checksumAlgorithmFactorySelector.selectList(
110                 ConfigUtils.parseCommaSeparatedUniqueNames(ConfigUtils.getString(
111                         session,
112                         DEFAULT_CHECKSUMS_ALGORITHMS,
113                         CONFIG_PROP_CHECKSUMS_ALGORITHMS + "." + repository.getId(),
114                         CONFIG_PROP_CHECKSUMS_ALGORITHMS,
115                         // MRESOLVER-701: support legacy properties for simpler transitioning
116                         "aether.checksums.algorithms",
117                         "aether.checksums.algorithms." + repository.getId())));
118 
119         return new Maven2RepositoryLayout(checksumsAlgorithms, artifactPredicateFactory.newInstance(session));
120     }
121 
122     private static class Maven2RepositoryLayout implements RepositoryLayout {
123         private final List<ChecksumAlgorithmFactory> configuredChecksumAlgorithms;
124         private final ArtifactPredicate artifactPredicate;
125 
126         private Maven2RepositoryLayout(
127                 List<ChecksumAlgorithmFactory> configuredChecksumAlgorithms, ArtifactPredicate artifactPredicate) {
128             this.configuredChecksumAlgorithms = Collections.unmodifiableList(configuredChecksumAlgorithms);
129             this.artifactPredicate = requireNonNull(artifactPredicate);
130         }
131 
132         private URI toUri(String path) {
133             try {
134                 return new URI(null, null, path, null);
135             } catch (URISyntaxException e) {
136                 throw new IllegalStateException(e);
137             }
138         }
139 
140         @Override
141         public List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories() {
142             return configuredChecksumAlgorithms;
143         }
144 
145         @Override
146         public boolean hasChecksums(Artifact artifact) {
147             return !artifactPredicate.isWithoutChecksum(artifact);
148         }
149 
150         @Override
151         public URI getLocation(Artifact artifact, boolean upload) {
152             StringBuilder path = new StringBuilder(128);
153 
154             path.append(artifact.getGroupId().replace('.', '/')).append('/');
155 
156             path.append(artifact.getArtifactId()).append('/');
157 
158             path.append(artifact.getBaseVersion()).append('/');
159 
160             path.append(artifact.getArtifactId()).append('-').append(artifact.getVersion());
161 
162             if (!artifact.getClassifier().isEmpty()) {
163                 path.append('-').append(artifact.getClassifier());
164             }
165 
166             if (!artifact.getExtension().isEmpty()) {
167                 path.append('.').append(artifact.getExtension());
168             }
169 
170             return toUri(path.toString());
171         }
172 
173         @Override
174         public URI getLocation(Metadata metadata, boolean upload) {
175             StringBuilder path = new StringBuilder(128);
176 
177             if (!metadata.getGroupId().isEmpty()) {
178                 path.append(metadata.getGroupId().replace('.', '/')).append('/');
179 
180                 if (!metadata.getArtifactId().isEmpty()) {
181                     path.append(metadata.getArtifactId()).append('/');
182 
183                     if (!metadata.getVersion().isEmpty()) {
184                         path.append(metadata.getVersion()).append('/');
185                     }
186                 }
187             }
188 
189             path.append(metadata.getType());
190 
191             return toUri(path.toString());
192         }
193 
194         @Override
195         public List<ChecksumLocation> getChecksumLocations(Artifact artifact, boolean upload, URI location) {
196             if (artifactPredicate.isWithoutChecksum(artifact) || artifactPredicate.isChecksum(artifact)) {
197                 return Collections.emptyList();
198             }
199             return getChecksumLocations(location);
200         }
201 
202         @Override
203         public List<ChecksumLocation> getChecksumLocations(Metadata metadata, boolean upload, URI location) {
204             return getChecksumLocations(location);
205         }
206 
207         private List<ChecksumLocation> getChecksumLocations(URI location) {
208             List<ChecksumLocation> checksumLocations = new ArrayList<>(configuredChecksumAlgorithms.size());
209             for (ChecksumAlgorithmFactory checksumAlgorithmFactory : configuredChecksumAlgorithms) {
210                 checksumLocations.add(ChecksumLocation.forLocation(location, checksumAlgorithmFactory));
211             }
212             return checksumLocations;
213         }
214     }
215 }