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