]> source.dussan.org Git - archiva.git/blob
79ecfaf899e1c03064d6defad6fa5fce1bf2aa92
[archiva.git] /
1 package org.apache.archiva.repository.maven.content;
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.repository.storage.RepositoryPathTranslator;
23 import org.apache.archiva.repository.RepositoryContent;
24 import org.apache.archiva.repository.content.ItemSelector;
25 import org.apache.archiva.repository.content.LayoutException;
26 import org.apache.archiva.repository.content.base.ArchivaItemSelector;
27 import org.apache.archiva.repository.maven.metadata.storage.ArtifactMappingProvider;
28 import org.apache.commons.lang3.StringUtils;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
31
32 import java.util.List;
33 import java.util.regex.Matcher;
34 import java.util.regex.Pattern;
35
36 /**
37  * AbstractDefaultRepositoryContent - common methods for working with default (maven 2) layout.
38  */
39 public abstract class AbstractDefaultRepositoryContent implements RepositoryContent
40 {
41
42
43     protected Logger log = LoggerFactory.getLogger( getClass() );
44
45     public static final String MAVEN_METADATA = "maven-metadata.xml";
46
47     protected static final char PATH_SEPARATOR = '/';
48
49     protected static final char GROUP_SEPARATOR = '.';
50
51     protected static final char ARTIFACT_SEPARATOR = '-';
52
53     private static final Pattern TIMESTAMP_PATTERN = Pattern.compile( "([0-9]{8}.[0-9]{6})-([0-9]+).*" );
54     private static final Pattern MAVEN_PLUGIN_PATTERN = Pattern.compile( "^(maven-.*-plugin)|(.*-maven-plugin)$" );
55
56     private RepositoryPathTranslator pathTranslator;
57     private List<? extends ArtifactMappingProvider> artifactMappingProviders;
58
59
60     AbstractDefaultRepositoryContent() {
61     }
62
63     public RepositoryPathTranslator getPathTranslator( )
64     {
65         return pathTranslator;
66     }
67
68     public void setPathTranslator( RepositoryPathTranslator pathTranslator )
69     {
70         this.pathTranslator = pathTranslator;
71     }
72
73     public void setArtifactMappingProviders(List<? extends ArtifactMappingProvider> artifactMappingProviders) {
74         this.artifactMappingProviders = artifactMappingProviders;
75     }
76
77     public ArchivaItemSelector.Builder getArtifactFromFilename( String namespace, String projectId, String projectVersion,
78                                                                 String artifactFileName )
79     {
80         if ( !artifactFileName.startsWith( projectId + "-" ) )
81         {
82             throw new IllegalArgumentException( "Not a valid artifact path in a Maven 2 repository, filename '" + artifactFileName
83                 + "' doesn't start with artifact ID '" + projectId + "'" );
84         }
85
86         int index = projectId.length() + 1;
87         String version;
88         String idSubStrFromVersion = artifactFileName.substring( index );
89         if ( idSubStrFromVersion.startsWith( projectVersion ) && !VersionUtil.isUniqueSnapshot( projectVersion ) )
90         {
91             // non-snapshot versions, or non-timestamped snapshot versions
92             version = projectVersion;
93         }
94         else if ( VersionUtil.isGenericSnapshot( projectVersion ) )
95         {
96             // timestamped snapshots
97             try
98             {
99                 int mainVersionLength = projectVersion.length() - 8; // 8 is length of "SNAPSHOT"
100                 if ( mainVersionLength == 0 )
101                 {
102                     throw new IllegalArgumentException(
103                         "Timestamped snapshots must contain the main version, filename was '" + artifactFileName + "'" );
104                 }
105
106                 Matcher m = TIMESTAMP_PATTERN.matcher( idSubStrFromVersion.substring( mainVersionLength ) );
107                 m.matches();
108                 String timestamp = m.group( 1 );
109                 String buildNumber = m.group( 2 );
110                 version = idSubStrFromVersion.substring( 0, mainVersionLength ) + timestamp + "-" + buildNumber;
111             }
112             catch ( IllegalStateException e )
113             {
114                 throw new IllegalArgumentException( "Not a valid artifact path in a Maven 2 repository, filename '" + artifactFileName
115                     + "' doesn't contain a timestamped version matching snapshot '"
116                     + projectVersion + "'", e);
117             }
118         }
119         else
120         {
121             // invalid
122             throw new IllegalArgumentException(
123                 "Not a valid artifact path in a Maven 2 repository, filename '" + artifactFileName + "' doesn't contain version '"
124                     + projectVersion + "'" );
125         }
126
127         String classifier;
128         String ext;
129         index += version.length();
130         if ( index == artifactFileName.length() )
131         {
132             // no classifier or extension
133             classifier = null;
134             ext = null;
135         }
136         else
137         {
138             char c = artifactFileName.charAt( index );
139             if ( c == '-' )
140             {
141                 // classifier up until '.'
142                 int extIndex = artifactFileName.indexOf( '.', index );
143                 if ( extIndex >= 0 )
144                 {
145                     classifier = artifactFileName.substring( index + 1, extIndex );
146                     ext = artifactFileName.substring( extIndex + 1 );
147                 }
148                 else
149                 {
150                     classifier = artifactFileName.substring( index + 1 );
151                     ext = null;
152                 }
153             }
154             else if ( c == '.' )
155             {
156                 // rest is the extension
157                 classifier = null;
158                 ext = artifactFileName.substring( index + 1 );
159             }
160             else
161             {
162                 throw new IllegalArgumentException( "Not a valid artifact path in a Maven 2 repository, filename '" + artifactFileName
163                     + "' expected classifier or extension but got '"
164                     + artifactFileName.substring( index ) + "'" );
165             }
166         }
167
168         ArchivaItemSelector.Builder selectorBuilder = ArchivaItemSelector.builder( )
169             .withNamespace( namespace )
170             .withProjectId( projectId )
171             .withArtifactId( projectId )
172             .withVersion( projectVersion )
173             .withArtifactVersion( version )
174             .withClassifier( classifier );
175
176
177         // we use our own provider here instead of directly accessing Maven's artifact handlers as it has no way
178         // to select the correct order to apply multiple extensions mappings to a preferred type
179         // TODO: this won't allow the user to decide order to apply them if there are conflicts or desired changes -
180         //       perhaps the plugins could register missing entries in configuration, then we just use configuration
181         //       here?
182
183         String type = null;
184         for ( ArtifactMappingProvider mapping : artifactMappingProviders )
185         {
186             type = mapping.mapClassifierAndExtensionToType( classifier, ext );
187             if ( type != null )
188             {
189                 break;
190             }
191         }
192
193         // TODO: this is cheating! We should check the POM metadata instead
194         if ( type == null && "jar".equals( ext ) && isArtifactIdValidMavenPlugin( projectId ) )
195         {
196             type = "maven-plugin";
197         }
198
199         // use extension as default
200         if ( type == null )
201         {
202             type = ext;
203         }
204
205         // TODO: should we allow this instead?
206         if ( type == null )
207         {
208             throw new IllegalArgumentException(
209                 "Not a valid artifact path in a Maven 2 repository, filename '" + artifactFileName + "' does not have a type" );
210         }
211
212         selectorBuilder.withType( type );
213
214
215         return selectorBuilder;
216     }
217
218     public boolean isArtifactIdValidMavenPlugin( String artifactId )
219     {
220         return MAVEN_PLUGIN_PATTERN.matcher( artifactId ).matches();
221     }
222
223     private ArchivaItemSelector getArtifactForPath( String relativePath )
224     {
225         String[] parts = relativePath.replace( '\\', '/' ).split( "/" );
226
227         int len = parts.length;
228         if ( len < 4 )
229         {
230             throw new IllegalArgumentException(
231                 "Not a valid artifact path in a Maven 2 repository, not enough directories: " + relativePath );
232         }
233
234         String fileName = parts[--len];
235         String baseVersion = parts[--len];
236         String artifactId = parts[--len];
237         StringBuilder namespaceBuilder = new StringBuilder();
238         for ( int i = 0; i < len - 1; i++ )
239         {
240             namespaceBuilder.append( parts[i] );
241             namespaceBuilder.append( '.' );
242         }
243         namespaceBuilder.append( parts[len - 1] );
244
245         return getArtifactFromFilename( namespaceBuilder.toString(), artifactId, baseVersion, fileName ).build();
246     }
247
248     @Override
249     public ItemSelector toItemSelector( String path ) throws LayoutException
250     {
251         if ( StringUtils.isBlank( path ) )
252         {
253             throw new LayoutException( "Unable to convert blank path." );
254         }
255         try
256         {
257
258             return getArtifactForPath( path );
259         }
260         catch ( IllegalArgumentException e )
261         {
262             throw new LayoutException( e.getMessage(), e );
263         }
264
265     }
266
267     @Override
268     public String toPath ( ItemSelector selector ) {
269         if (selector==null) {
270             throw new IllegalArgumentException( "ItemSelector must not be null." );
271         }
272         String projectId;
273         // Initialize the project id if not set
274         if (selector.hasProjectId()) {
275             projectId = selector.getProjectId( );
276         } else if (selector.hasArtifactId()) {
277             // projectId same as artifact id, if set
278             projectId = selector.getArtifactId( );
279         } else {
280             // we arrive here, if projectId && artifactId not set
281             return pathTranslator.toPath( selector.getNamespace(), "");
282         }
283         if ( !selector.hasArtifactId( )) {
284             return pathTranslator.toPath( selector.getNamespace( ), projectId );
285         }
286         // this part only, if projectId && artifactId is set
287         String artifactVersion = "";
288         String version = "";
289         if (selector.hasVersion() && selector.hasArtifactVersion() ) {
290             artifactVersion = selector.getArtifactVersion();
291             version = VersionUtil.getBaseVersion( selector.getVersion( ) );
292         } else if (!selector.hasVersion() && selector.hasArtifactVersion()) {
293             // we try to retrieve the base version, if artifact version is only set
294             version = VersionUtil.getBaseVersion( selector.getArtifactVersion( ) );
295             artifactVersion = selector.getArtifactVersion( );
296         } else if (selector.hasVersion() && !selector.hasArtifactVersion()) {
297             artifactVersion = selector.getVersion();
298             version = VersionUtil.getBaseVersion( selector.getVersion( ) );
299         }
300
301         return pathTranslator.toPath( selector.getNamespace(), projectId, version,
302                 constructId( selector.getArtifactId(), artifactVersion, selector.getClassifier(), selector.getType() ) );
303
304     }
305
306
307     public String toPath( String namespace )
308     {
309         return formatAsDirectory( namespace );
310     }
311
312
313     protected String formatAsDirectory( String directory )
314     {
315         return directory.replace( GROUP_SEPARATOR, PATH_SEPARATOR );
316     }
317
318     private String toPath( String groupId, String artifactId, String baseVersion, String version, String classifier,
319                            String type )
320     {
321         if ( baseVersion != null )
322         {
323             return pathTranslator.toPath( groupId, artifactId, baseVersion,
324                                           constructId( artifactId, version, classifier, type ) );
325         }
326         else
327         {
328             return pathTranslator.toPath( groupId, artifactId );
329         }
330     }
331
332     // TODO: move into the Maven Artifact facet when refactoring away the caller - the caller will need to have access
333     //       to the facet or filename (for the original ID)
334     private String constructId( String artifactId, String version, String classifier, String type )
335     {
336         String ext = null;
337         for ( ArtifactMappingProvider provider : artifactMappingProviders )
338         {
339             ext = provider.mapTypeToExtension( type );
340             if ( ext != null )
341             {
342                 break;
343             }
344         }
345         if ( ext == null )
346         {
347             ext = type;
348         }
349
350         StringBuilder id = new StringBuilder();
351         if ( ( version != null ) && ( type != null ) )
352         {
353             id.append( artifactId ).append( ARTIFACT_SEPARATOR ).append( version );
354
355             if ( StringUtils.isNotBlank( classifier ) )
356             {
357                 id.append( ARTIFACT_SEPARATOR ).append( classifier );
358             }
359
360             id.append( "." ).append( ext );
361         }
362         return id.toString();
363     }
364 }