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