]> source.dussan.org Git - archiva.git/blob
b910368091c8d61b47e08859fbb16c65ccd44e9c
[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.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.nio.file.Path;
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 Path toFile(Path basedir, String namespace, String projectId, String projectVersion, String filename )
82     {
83         return basedir.resolve( toPath( namespace, projectId, projectVersion, filename ) );
84     }
85
86     @Override
87     public Path toFile( Path basedir, String namespace, String projectId, String projectVersion )
88     {
89         return basedir.resolve( 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 Path toFile( Path basedir, String namespace, String projectId )
152     {
153         return basedir.resolve( toPath( namespace, projectId ) );
154     }
155
156     @Override
157     public Path toFile( Path basedir, String namespace )
158     {
159         return basedir.resolve( 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 }