This project has retired. For details please refer to its Attic page.
Maven2RepositoryPathTranslator xref
View Javadoc
1   package org.apache.archiva.metadata.repository.storage.maven2;
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 org.apache.archiva.metadata.model.ArtifactMetadata;
23  import org.apache.archiva.metadata.model.maven2.MavenArtifactFacet;
24  import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
25  import org.apache.archiva.common.utils.VersionUtil;
26  import org.slf4j.Logger;
27  import org.slf4j.LoggerFactory;
28  import org.springframework.stereotype.Service;
29  
30  import javax.annotation.PostConstruct;
31  import javax.inject.Inject;
32  import java.io.File;
33  import java.util.List;
34  import java.util.regex.Matcher;
35  import java.util.regex.Pattern;
36  
37  /**
38   *
39   */
40  @Service( "repositoryPathTranslator#maven2" )
41  public class Maven2RepositoryPathTranslator
42      implements RepositoryPathTranslator
43  {
44  
45      private Logger log = LoggerFactory.getLogger( getClass() );
46  
47      private static final char GROUP_SEPARATOR = '.';
48  
49      private static final Pattern TIMESTAMP_PATTERN = Pattern.compile( "([0-9]{8}.[0-9]{6})-([0-9]+).*" );
50      
51  
52      private static final Pattern MAVEN_PLUGIN_PATTERN = Pattern.compile( "^(maven-.*-plugin)|(.*-maven-plugin)$" );    
53  
54      /**
55       *
56       * see #initialize
57       */
58      @Inject
59      private List<ArtifactMappingProvider> artifactMappingProviders;
60  
61      public Maven2RepositoryPathTranslator()
62      {
63          // noop
64      }
65  
66      @PostConstruct
67      public void initialize()
68      {
69          //artifactMappingProviders = new ArrayList<ArtifactMappingProvider>(
70          //    applicationContext.getBeansOfType( ArtifactMappingProvider.class ).values() );
71  
72      }
73  
74  
75      public Maven2RepositoryPathTranslator( List<ArtifactMappingProvider> artifactMappingProviders )
76      {
77          this.artifactMappingProviders = artifactMappingProviders;
78      }
79  
80      @Override
81      public File toFile( File basedir, String namespace, String projectId, String projectVersion, String filename )
82      {
83          return new File( basedir, toPath( namespace, projectId, projectVersion, filename ) );
84      }
85  
86      @Override
87      public File toFile( File basedir, String namespace, String projectId, String projectVersion )
88      {
89          return new File( basedir, toPath( namespace, projectId, projectVersion ) );
90      }
91  
92      @Override
93      public String toPath( String namespace, String projectId, String projectVersion, String filename )
94      {
95          StringBuilder path = new StringBuilder();
96  
97          appendNamespaceToProjectVersion( path, namespace, projectId, projectVersion );
98          path.append( PATH_SEPARATOR );
99          path.append( filename );
100 
101         return path.toString();
102     }
103 
104     private void appendNamespaceToProjectVersion( StringBuilder path, String namespace, String projectId,
105                                                   String projectVersion )
106     {
107         appendNamespaceAndProject( path, namespace, projectId );
108         path.append( projectVersion );
109     }
110 
111     public String toPath( String namespace, String projectId, String projectVersion )
112     {
113         StringBuilder path = new StringBuilder();
114 
115         appendNamespaceToProjectVersion( path, namespace, projectId, projectVersion );
116 
117         return path.toString();
118     }
119 
120     public String toPath( String namespace )
121     {
122         StringBuilder path = new StringBuilder();
123 
124         appendNamespace( path, namespace );
125 
126         return path.toString();
127     }
128 
129     @Override
130     public String toPath( String namespace, String projectId )
131     {
132         StringBuilder path = new StringBuilder();
133 
134         appendNamespaceAndProject( path, namespace, projectId );
135 
136         return path.toString();
137     }
138 
139     private void appendNamespaceAndProject( StringBuilder path, String namespace, String projectId )
140     {
141         appendNamespace( path, namespace );
142         path.append( projectId ).append( PATH_SEPARATOR );
143     }
144 
145     private void appendNamespace( StringBuilder path, String namespace )
146     {
147         path.append( formatAsDirectory( namespace ) ).append( PATH_SEPARATOR );
148     }
149 
150     @Override
151     public File toFile( File basedir, String namespace, String projectId )
152     {
153         return new File( basedir, toPath( namespace, projectId ) );
154     }
155 
156     @Override
157     public File toFile( File basedir, String namespace )
158     {
159         return new File( basedir, toPath( namespace ) );
160     }
161 
162     private String formatAsDirectory( String directory )
163     {
164         return directory.replace( GROUP_SEPARATOR, PATH_SEPARATOR );
165     }
166 
167     @Override
168     public ArtifactMetadata getArtifactForPath( String repoId, String relativePath )
169     {
170         String[] parts = relativePath.replace( '\\', '/' ).split( "/" );
171 
172         int len = parts.length;
173         if ( len < 4 )
174         {
175             throw new IllegalArgumentException(
176                 "Not a valid artifact path in a Maven 2 repository, not enough directories: " + relativePath );
177         }
178 
179         String id = parts[--len];
180         String baseVersion = parts[--len];
181         String artifactId = parts[--len];
182         StringBuilder groupIdBuilder = new StringBuilder();
183         for ( int i = 0; i < len - 1; i++ )
184         {
185             groupIdBuilder.append( parts[i] );
186             groupIdBuilder.append( '.' );
187         }
188         groupIdBuilder.append( parts[len - 1] );
189 
190         return getArtifactFromId( repoId, groupIdBuilder.toString(), artifactId, baseVersion, id );
191     }
192 
193     @Override
194     public ArtifactMetadata getArtifactFromId( String repoId, String namespace, String projectId, String projectVersion,
195                                                String id )
196     {
197         if ( !id.startsWith( projectId + "-" ) )
198         {
199             throw new IllegalArgumentException( "Not a valid artifact path in a Maven 2 repository, filename '" + id
200                                                     + "' doesn't start with artifact ID '" + projectId + "'" );
201         }
202 
203         MavenArtifactFacet facet = new MavenArtifactFacet();
204 
205         int index = projectId.length() + 1;
206         String version;
207         String idSubStrFromVersion = id.substring( index );
208         if ( idSubStrFromVersion.startsWith( projectVersion ) && !VersionUtil.isUniqueSnapshot( projectVersion ) )
209         {
210             // non-snapshot versions, or non-timestamped snapshot versions
211             version = projectVersion;
212         }
213         else if ( VersionUtil.isGenericSnapshot( projectVersion ) )
214         {
215             // timestamped snapshots
216             try
217             {
218                 int mainVersionLength = projectVersion.length() - 8; // 8 is length of "SNAPSHOT"
219                 if ( mainVersionLength == 0 )
220                 {
221                     throw new IllegalArgumentException(
222                         "Timestamped snapshots must contain the main version, filename was '" + id + "'" );
223                 }
224 
225                 Matcher m = TIMESTAMP_PATTERN.matcher( idSubStrFromVersion.substring( mainVersionLength ) );
226                 m.matches();
227                 String timestamp = m.group( 1 );
228                 String buildNumber = m.group( 2 );
229                 facet.setTimestamp( timestamp );
230                 facet.setBuildNumber( Integer.parseInt( buildNumber ) );
231                 version = idSubStrFromVersion.substring( 0, mainVersionLength ) + timestamp + "-" + buildNumber;
232             }
233             catch ( IllegalStateException e )
234             {
235                 throw new IllegalArgumentException( "Not a valid artifact path in a Maven 2 repository, filename '" + id
236                                                         + "' doesn't contain a timestamped version matching snapshot '"
237                                                         + projectVersion + "'", e);
238             }
239         }
240         else
241         {
242             // invalid
243             throw new IllegalArgumentException(
244                 "Not a valid artifact path in a Maven 2 repository, filename '" + id + "' doesn't contain version '"
245                     + projectVersion + "'" );
246         }
247 
248         String classifier;
249         String ext;
250         index += version.length();
251         if ( index == id.length() )
252         {
253             // no classifier or extension
254             classifier = null;
255             ext = null;
256         }
257         else
258         {
259             char c = id.charAt( index );
260             if ( c == '-' )
261             {
262                 // classifier up until '.'
263                 int extIndex = id.indexOf( '.', index );
264                 if ( extIndex >= 0 )
265                 {
266                     classifier = id.substring( index + 1, extIndex );
267                     ext = id.substring( extIndex + 1 );
268                 }
269                 else
270                 {
271                     classifier = id.substring( index + 1 );
272                     ext = null;
273                 }
274             }
275             else if ( c == '.' )
276             {
277                 // rest is the extension
278                 classifier = null;
279                 ext = id.substring( index + 1 );
280             }
281             else
282             {
283                 throw new IllegalArgumentException( "Not a valid artifact path in a Maven 2 repository, filename '" + id
284                                                         + "' expected classifier or extension but got '"
285                                                         + id.substring( index ) + "'" );
286             }
287         }
288 
289         ArtifactMetadata metadata = new ArtifactMetadata();
290         metadata.setId( id );
291         metadata.setNamespace( namespace );
292         metadata.setProject( projectId );
293         metadata.setRepositoryId( repoId );
294         metadata.setProjectVersion( projectVersion );
295         metadata.setVersion( version );
296 
297         facet.setClassifier( classifier );
298 
299         // we use our own provider here instead of directly accessing Maven's artifact handlers as it has no way
300         // to select the correct order to apply multiple extensions mappings to a preferred type
301         // TODO: this won't allow the user to decide order to apply them if there are conflicts or desired changes -
302         //       perhaps the plugins could register missing entries in configuration, then we just use configuration
303         //       here?
304 
305         String type = null;
306         for ( ArtifactMappingProvider mapping : artifactMappingProviders )
307         {
308             type = mapping.mapClassifierAndExtensionToType( classifier, ext );
309             if ( type != null )
310             {
311                 break;
312             }
313         }
314 
315         // TODO: this is cheating! We should check the POM metadata instead
316         if ( type == null && "jar".equals( ext ) && isArtifactIdValidMavenPlugin( projectId ) )
317         {
318             type = "maven-plugin";
319         }
320 
321         // use extension as default
322         if ( type == null )
323         {
324             type = ext;
325         }
326 
327         // TODO: should we allow this instead?
328         if ( type == null )
329         {
330             throw new IllegalArgumentException(
331                 "Not a valid artifact path in a Maven 2 repository, filename '" + id + "' does not have a type" );
332         }
333 
334         facet.setType( type );
335         metadata.addFacet( facet );
336 
337         return metadata;
338     }
339 
340 
341     public boolean isArtifactIdValidMavenPlugin( String artifactId )
342     {
343         return MAVEN_PLUGIN_PATTERN.matcher( artifactId ).matches();
344     }
345 }