]> source.dussan.org Git - archiva.git/blob
eeb2000427b4ca7c93b7d49bb2f8026600a978b4
[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.model.ArtifactMetadata;
23 import org.apache.archiva.metadata.maven.model.MavenArtifactFacet;
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 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 StorageAsset toFile(StorageAsset 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 StorageAsset toFile( StorageAsset 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         if (StringUtils.isNotEmpty( projectId ))
143         {
144             path.append( projectId ).append( PATH_SEPARATOR );
145         }
146     }
147
148     private void appendNamespace( StringBuilder path, String namespace )
149     {
150         if ( StringUtils.isNotEmpty( namespace ) ) {
151             path.append( formatAsDirectory( namespace ) ).append( PATH_SEPARATOR );
152         }
153     }
154
155     @Override
156     public StorageAsset toFile( StorageAsset basedir, String namespace, String projectId )
157     {
158         return basedir.resolve( toPath( namespace, projectId ) );
159     }
160
161     @Override
162     public StorageAsset toFile( StorageAsset basedir, String namespace )
163     {
164         return basedir.resolve( toPath( namespace ) );
165     }
166
167     private String formatAsDirectory( String directory )
168     {
169         return directory.replace( GROUP_SEPARATOR, PATH_SEPARATOR );
170     }
171
172     @Override
173     public ArtifactMetadata getArtifactForPath( String repoId, String relativePath )
174     {
175         String[] parts = relativePath.replace( '\\', '/' ).split( "/" );
176
177         int len = parts.length;
178         if ( len < 4 )
179         {
180             throw new IllegalArgumentException(
181                 "Not a valid artifact path in a Maven 2 repository, not enough directories: " + relativePath );
182         }
183
184         String id = parts[--len];
185         String baseVersion = parts[--len];
186         String artifactId = parts[--len];
187         StringBuilder groupIdBuilder = new StringBuilder();
188         for ( int i = 0; i < len - 1; i++ )
189         {
190             groupIdBuilder.append( parts[i] );
191             groupIdBuilder.append( '.' );
192         }
193         groupIdBuilder.append( parts[len - 1] );
194
195         return getArtifactFromId( repoId, groupIdBuilder.toString(), artifactId, baseVersion, id );
196     }
197
198     @Override
199     public ArtifactMetadata getArtifactFromId( String repoId, String namespace, String projectId, String projectVersion,
200                                                String id )
201     {
202         if ( !id.startsWith( projectId + "-" ) )
203         {
204             throw new IllegalArgumentException( "Not a valid artifact path in a Maven 2 repository, filename '" + id
205                                                     + "' doesn't start with artifact ID '" + projectId + "'" );
206         }
207
208         MavenArtifactFacet facet = new MavenArtifactFacet();
209
210         int index = projectId.length() + 1;
211         String version;
212         String idSubStrFromVersion = id.substring( index );
213         if ( idSubStrFromVersion.startsWith( projectVersion ) && !VersionUtil.isUniqueSnapshot( projectVersion ) )
214         {
215             // non-snapshot versions, or non-timestamped snapshot versions
216             version = projectVersion;
217         }
218         else if ( VersionUtil.isGenericSnapshot( projectVersion ) )
219         {
220             // timestamped snapshots
221             try
222             {
223                 int mainVersionLength = projectVersion.length() - 8; // 8 is length of "SNAPSHOT"
224                 if ( mainVersionLength == 0 )
225                 {
226                     throw new IllegalArgumentException(
227                         "Timestamped snapshots must contain the main version, filename was '" + id + "'" );
228                 }
229
230                 Matcher m = TIMESTAMP_PATTERN.matcher( idSubStrFromVersion.substring( mainVersionLength ) );
231                 m.matches();
232                 String timestamp = m.group( 1 );
233                 String buildNumber = m.group( 2 );
234                 facet.setTimestamp( timestamp );
235                 facet.setBuildNumber( Integer.parseInt( buildNumber ) );
236                 version = idSubStrFromVersion.substring( 0, mainVersionLength ) + timestamp + "-" + buildNumber;
237             }
238             catch ( IllegalStateException e )
239             {
240                 throw new IllegalArgumentException( "Not a valid artifact path in a Maven 2 repository, filename '" + id
241                                                         + "' doesn't contain a timestamped version matching snapshot '"
242                                                         + projectVersion + "'", e);
243             }
244         }
245         else
246         {
247             // invalid
248             throw new IllegalArgumentException(
249                 "Not a valid artifact path in a Maven 2 repository, filename '" + id + "' doesn't contain version '"
250                     + projectVersion + "'" );
251         }
252
253         String classifier;
254         String ext;
255         index += version.length();
256         if ( index == id.length() )
257         {
258             // no classifier or extension
259             classifier = null;
260             ext = null;
261         }
262         else
263         {
264             char c = id.charAt( index );
265             if ( c == '-' )
266             {
267                 // classifier up until '.'
268                 int extIndex = id.indexOf( '.', index );
269                 if ( extIndex >= 0 )
270                 {
271                     classifier = id.substring( index + 1, extIndex );
272                     ext = id.substring( extIndex + 1 );
273                 }
274                 else
275                 {
276                     classifier = id.substring( index + 1 );
277                     ext = null;
278                 }
279             }
280             else if ( c == '.' )
281             {
282                 // rest is the extension
283                 classifier = null;
284                 ext = id.substring( index + 1 );
285             }
286             else
287             {
288                 throw new IllegalArgumentException( "Not a valid artifact path in a Maven 2 repository, filename '" + id
289                                                         + "' expected classifier or extension but got '"
290                                                         + id.substring( index ) + "'" );
291             }
292         }
293
294         ArtifactMetadata metadata = new ArtifactMetadata();
295         metadata.setId( id );
296         metadata.setNamespace( namespace );
297         metadata.setProject( projectId );
298         metadata.setRepositoryId( repoId );
299         metadata.setProjectVersion( projectVersion );
300         metadata.setVersion( version );
301
302         facet.setClassifier( classifier );
303
304         // we use our own provider here instead of directly accessing Maven's artifact handlers as it has no way
305         // to select the correct order to apply multiple extensions mappings to a preferred type
306         // TODO: this won't allow the user to decide order to apply them if there are conflicts or desired changes -
307         //       perhaps the plugins could register missing entries in configuration, then we just use configuration
308         //       here?
309
310         String type = null;
311         for ( ArtifactMappingProvider mapping : artifactMappingProviders )
312         {
313             type = mapping.mapClassifierAndExtensionToType( classifier, ext );
314             if ( type != null )
315             {
316                 break;
317             }
318         }
319
320         // TODO: this is cheating! We should check the POM metadata instead
321         if ( type == null && "jar".equals( ext ) && isArtifactIdValidMavenPlugin( projectId ) )
322         {
323             type = "maven-plugin";
324         }
325
326         // use extension as default
327         if ( type == null )
328         {
329             type = ext;
330         }
331
332         // TODO: should we allow this instead?
333         if ( type == null )
334         {
335             throw new IllegalArgumentException(
336                 "Not a valid artifact path in a Maven 2 repository, filename '" + id + "' does not have a type" );
337         }
338
339         facet.setType( type );
340         metadata.addFacet( facet );
341
342         return metadata;
343     }
344
345
346     public boolean isArtifactIdValidMavenPlugin( String artifactId )
347     {
348         return MAVEN_PLUGIN_PATTERN.matcher( artifactId ).matches();
349     }
350 }