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.LinkedHashSet;
28  import java.util.List;
29  import java.util.Set;
30  import java.util.stream.Collectors;
31  
32  import javax.inject.Inject;
33  import javax.inject.Named;
34  import javax.inject.Singleton;
35  
36  import org.eclipse.aether.RepositorySystemSession;
37  import org.eclipse.aether.artifact.Artifact;
38  import org.eclipse.aether.internal.impl.checksum.DefaultChecksumAlgorithmFactorySelector;
39  import org.eclipse.aether.metadata.Metadata;
40  import org.eclipse.aether.repository.RemoteRepository;
41  import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
42  import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactorySelector;
43  import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
44  import org.eclipse.aether.spi.connector.layout.RepositoryLayoutFactory;
45  import org.eclipse.aether.transfer.NoRepositoryLayoutException;
46  import org.eclipse.aether.util.ConfigUtils;
47  
48  import static java.util.Objects.requireNonNull;
49  
50  /**
51   * Provides a Maven-2 repository layout for repositories with content type {@code "default"}.
52   */
53  @Singleton
54  @Named( "maven2" )
55  public final class Maven2RepositoryLayoutFactory
56          implements RepositoryLayoutFactory
57  {
58  
59      public static final String CONFIG_PROP_CHECKSUMS_ALGORITHMS = "aether.checksums.algorithms";
60  
61      private static final String DEFAULT_CHECKSUMS_ALGORITHMS = "SHA-1,MD5";
62  
63      public static final String CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS =
64              "aether.checksums.omitChecksumsForExtensions";
65  
66      private static final String DEFAULT_OMIT_CHECKSUMS_FOR_EXTENSIONS = ".asc";
67  
68      private float priority;
69  
70      private final ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector;
71  
72      public float getPriority()
73      {
74          return priority;
75      }
76  
77      /**
78       * Service locator ctor.
79       */
80      @Deprecated
81      public Maven2RepositoryLayoutFactory()
82      {
83          this( new DefaultChecksumAlgorithmFactorySelector() );
84      }
85  
86      @Inject
87      public Maven2RepositoryLayoutFactory( ChecksumAlgorithmFactorySelector checksumAlgorithmFactorySelector )
88      {
89          this.checksumAlgorithmFactorySelector = requireNonNull( checksumAlgorithmFactorySelector );
90      }
91  
92      /**
93       * Sets the priority of this component.
94       *
95       * @param priority The priority.
96       * @return This component for chaining, never {@code null}.
97       */
98      public Maven2RepositoryLayoutFactory setPriority( float priority )
99      {
100         this.priority = priority;
101         return this;
102     }
103 
104     public RepositoryLayout newInstance( RepositorySystemSession session, RemoteRepository repository )
105             throws NoRepositoryLayoutException
106     {
107         requireNonNull( session, "session cannot be null" );
108         requireNonNull( repository, "repository cannot be null" );
109         if ( !"default".equals( repository.getContentType() ) )
110         {
111             throw new NoRepositoryLayoutException( repository );
112         }
113         // ensure order and uniqueness of (potentially user set) algorithm list
114         LinkedHashSet<String> checksumsAlgorithmNames = Arrays.stream( ConfigUtils.getString(
115                         session, DEFAULT_CHECKSUMS_ALGORITHMS, CONFIG_PROP_CHECKSUMS_ALGORITHMS )
116                 .split( "," )
117         ).filter( s -> s != null && !s.trim().isEmpty() ).collect( Collectors.toCollection( LinkedHashSet::new ) );
118 
119         // validation: this loop implicitly validates the list above: selector will throw on unknown algorithm
120         List<ChecksumAlgorithmFactory> checksumsAlgorithms = new ArrayList<>( checksumsAlgorithmNames.size() );
121         for ( String checksumsAlgorithmName : checksumsAlgorithmNames )
122         {
123             checksumsAlgorithms.add( checksumAlgorithmFactorySelector.select( checksumsAlgorithmName ) );
124         }
125 
126         // ensure uniqueness of (potentially user set) extension list
127         Set<String> omitChecksumsForExtensions = Arrays.stream( ConfigUtils.getString(
128                         session, DEFAULT_OMIT_CHECKSUMS_FOR_EXTENSIONS, CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS )
129                 .split( "," )
130         ).filter( s -> s != null && !s.trim().isEmpty() ).collect( Collectors.toSet() );
131 
132         // validation: enforce that all strings in this set are having leading dot
133         if ( omitChecksumsForExtensions.stream().anyMatch( s -> !s.startsWith( "." ) ) )
134         {
135             throw new IllegalArgumentException(
136                     String.format(
137                             "The configuration %s contains illegal values: %s (all entries must start with '.' (dot))",
138                             CONFIG_PROP_OMIT_CHECKSUMS_FOR_EXTENSIONS,
139                             omitChecksumsForExtensions
140                     )
141             );
142         }
143 
144         return new Maven2RepositoryLayout(
145                 checksumsAlgorithms,
146                 omitChecksumsForExtensions
147         );
148     }
149 
150     private static class Maven2RepositoryLayout
151             implements RepositoryLayout
152     {
153 
154         private final List<ChecksumAlgorithmFactory> checksumAlgorithms;
155 
156         private final Set<String> extensionsWithoutChecksums;
157 
158         private Maven2RepositoryLayout( List<ChecksumAlgorithmFactory> checksumAlgorithms,
159                                         Set<String> extensionsWithoutChecksums )
160         {
161             this.checksumAlgorithms = Collections.unmodifiableList( checksumAlgorithms );
162             this.extensionsWithoutChecksums = requireNonNull( extensionsWithoutChecksums );
163         }
164 
165         private URI toUri( String path )
166         {
167             try
168             {
169                 return new URI( null, null, path, null );
170             }
171             catch ( URISyntaxException e )
172             {
173                 throw new IllegalStateException( e );
174             }
175         }
176 
177         @Override
178         public List<ChecksumAlgorithmFactory> getChecksumAlgorithmFactories()
179         {
180             return checksumAlgorithms;
181         }
182 
183         @Override
184         public boolean hasChecksums( Artifact artifact )
185         {
186             String artifactExtension = artifact.getExtension(); // ie. pom.asc
187             for ( String extensionWithoutChecksums : extensionsWithoutChecksums )
188             {
189                 if ( artifactExtension.endsWith( extensionWithoutChecksums ) )
190                 {
191                     return false;
192                 }
193             }
194             return true;
195         }
196 
197         @Override
198         public URI getLocation( Artifact artifact, boolean upload )
199         {
200             StringBuilder path = new StringBuilder( 128 );
201 
202             path.append( artifact.getGroupId().replace( '.', '/' ) ).append( '/' );
203 
204             path.append( artifact.getArtifactId() ).append( '/' );
205 
206             path.append( artifact.getBaseVersion() ).append( '/' );
207 
208             path.append( artifact.getArtifactId() ).append( '-' ).append( artifact.getVersion() );
209 
210             if ( artifact.getClassifier().length() > 0 )
211             {
212                 path.append( '-' ).append( artifact.getClassifier() );
213             }
214 
215             if ( artifact.getExtension().length() > 0 )
216             {
217                 path.append( '.' ).append( artifact.getExtension() );
218             }
219 
220             return toUri( path.toString() );
221         }
222 
223         @Override
224         public URI getLocation( Metadata metadata, boolean upload )
225         {
226             StringBuilder path = new StringBuilder( 128 );
227 
228             if ( metadata.getGroupId().length() > 0 )
229             {
230                 path.append( metadata.getGroupId().replace( '.', '/' ) ).append( '/' );
231 
232                 if ( metadata.getArtifactId().length() > 0 )
233                 {
234                     path.append( metadata.getArtifactId() ).append( '/' );
235 
236                     if ( metadata.getVersion().length() > 0 )
237                     {
238                         path.append( metadata.getVersion() ).append( '/' );
239                     }
240                 }
241             }
242 
243             path.append( metadata.getType() );
244 
245             return toUri( path.toString() );
246         }
247 
248         @Override
249         public List<ChecksumLocation> getChecksumLocations( Artifact artifact, boolean upload, URI location )
250         {
251             if ( !hasChecksums( artifact ) || isChecksum( artifact.getExtension() ) )
252             {
253                 return Collections.emptyList();
254             }
255             return getChecksumLocations( location );
256         }
257 
258         @Override
259         public List<ChecksumLocation> getChecksumLocations( Metadata metadata, boolean upload, URI location )
260         {
261             return getChecksumLocations( location );
262         }
263 
264         private List<ChecksumLocation> getChecksumLocations( URI location )
265         {
266             List<ChecksumLocation> checksumLocations = new ArrayList<>( checksumAlgorithms.size() );
267             for ( ChecksumAlgorithmFactory checksumAlgorithmFactory : checksumAlgorithms )
268             {
269                 checksumLocations.add( ChecksumLocation.forLocation( location, checksumAlgorithmFactory ) );
270             }
271             return checksumLocations;
272         }
273 
274         private boolean isChecksum( String extension )
275         {
276             return checksumAlgorithms.stream().anyMatch( a -> extension.endsWith( "." + a.getFileExtension() ) );
277         }
278     }
279 }