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