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 
116         return new Maven2RepositoryLayout(checksumsAlgorithms, artifactPredicateFactory.newInstance(session));
117     }
118 
119     private static class Maven2RepositoryLayout implements RepositoryLayout {
120         private final List<ChecksumAlgorithmFactory> configuredChecksumAlgorithms;
121         private final ArtifactPredicate artifactPredicate;
122 
123         private Maven2RepositoryLayout(
124                 List<ChecksumAlgorithmFactory> configuredChecksumAlgorithms, ArtifactPredicate artifactPredicate) {
125             this.configuredChecksumAlgorithms = Collections.unmodifiableList(configuredChecksumAlgorithms);
126             this.artifactPredicate = requireNonNull(artifactPredicate);
127         }
128 
129         private URI toUri(String path) {
130             try {
131                 return new URI(null, null, path, null);
132             } catch (URISyntaxException e) {
133                 throw new IllegalStateException(e);
134             }
135         }
136 
137         @Override
138         public List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories() {
139             return configuredChecksumAlgorithms;
140         }
141 
142         @Override
143         public boolean hasChecksums(Artifact artifact) {
144             return !artifactPredicate.isWithoutChecksum(artifact);
145         }
146 
147         @Override
148         public URI getLocation(Artifact artifact, boolean upload) {
149             StringBuilder path = new StringBuilder(128);
150 
151             path.append(artifact.getGroupId().replace('.', '/')).append('/');
152 
153             path.append(artifact.getArtifactId()).append('/');
154 
155             path.append(artifact.getBaseVersion()).append('/');
156 
157             path.append(artifact.getArtifactId()).append('-').append(artifact.getVersion());
158 
159             if (!artifact.getClassifier().isEmpty()) {
160                 path.append('-').append(artifact.getClassifier());
161             }
162 
163             if (!artifact.getExtension().isEmpty()) {
164                 path.append('.').append(artifact.getExtension());
165             }
166 
167             return toUri(path.toString());
168         }
169 
170         @Override
171         public URI getLocation(Metadata metadata, boolean upload) {
172             StringBuilder path = new StringBuilder(128);
173 
174             if (!metadata.getGroupId().isEmpty()) {
175                 path.append(metadata.getGroupId().replace('.', '/')).append('/');
176 
177                 if (!metadata.getArtifactId().isEmpty()) {
178                     path.append(metadata.getArtifactId()).append('/');
179 
180                     if (!metadata.getVersion().isEmpty()) {
181                         path.append(metadata.getVersion()).append('/');
182                     }
183                 }
184             }
185 
186             path.append(metadata.getType());
187 
188             return toUri(path.toString());
189         }
190 
191         @Override
192         public List<ChecksumLocation> getChecksumLocations(Artifact artifact, boolean upload, URI location) {
193             if (artifactPredicate.isWithoutChecksum(artifact) || artifactPredicate.isChecksum(artifact)) {
194                 return Collections.emptyList();
195             }
196             return getChecksumLocations(location);
197         }
198 
199         @Override
200         public List<ChecksumLocation> getChecksumLocations(Metadata metadata, boolean upload, URI location) {
201             return getChecksumLocations(location);
202         }
203 
204         private List<ChecksumLocation> getChecksumLocations(URI location) {
205             List<ChecksumLocation> checksumLocations = new ArrayList<>(configuredChecksumAlgorithms.size());
206             for (ChecksumAlgorithmFactory checksumAlgorithmFactory : configuredChecksumAlgorithms) {
207                 checksumLocations.add(ChecksumLocation.forLocation(location, checksumAlgorithmFactory));
208             }
209             return checksumLocations;
210         }
211     }
212 }