This project has retired. For details please refer to its Attic page.
Source code
001package org.apache.archiva.metadata.repository.storage.maven2;
002
003/*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements.  See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership.  The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License.  You may obtain a copy of the License at
011 *
012 *   http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied.  See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022import org.apache.archiva.metadata.model.ArtifactMetadata;
023import org.apache.archiva.metadata.model.maven2.MavenArtifactFacet;
024import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
025import org.apache.archiva.common.utils.VersionUtil;
026import org.slf4j.Logger;
027import org.slf4j.LoggerFactory;
028import org.springframework.stereotype.Service;
029
030import javax.annotation.PostConstruct;
031import javax.inject.Inject;
032import java.io.File;
033import java.util.List;
034import java.util.regex.Matcher;
035import java.util.regex.Pattern;
036
037/**
038 *
039 */
040@Service( "repositoryPathTranslator#maven2" )
041public class Maven2RepositoryPathTranslator
042    implements RepositoryPathTranslator
043{
044
045    private Logger log = LoggerFactory.getLogger( getClass() );
046
047    private static final char GROUP_SEPARATOR = '.';
048
049    private static final Pattern TIMESTAMP_PATTERN = Pattern.compile( "([0-9]{8}.[0-9]{6})-([0-9]+).*" );
050    
051
052    private static final Pattern MAVEN_PLUGIN_PATTERN = Pattern.compile( "^(maven-.*-plugin)|(.*-maven-plugin)$" );    
053
054    /**
055     *
056     * see #initialize
057     */
058    @Inject
059    private List<ArtifactMappingProvider> artifactMappingProviders;
060
061    public Maven2RepositoryPathTranslator()
062    {
063        // noop
064    }
065
066    @PostConstruct
067    public void initialize()
068    {
069        //artifactMappingProviders = new ArrayList<ArtifactMappingProvider>(
070        //    applicationContext.getBeansOfType( ArtifactMappingProvider.class ).values() );
071
072    }
073
074
075    public Maven2RepositoryPathTranslator( List<ArtifactMappingProvider> artifactMappingProviders )
076    {
077        this.artifactMappingProviders = artifactMappingProviders;
078    }
079
080    @Override
081    public File toFile( File basedir, String namespace, String projectId, String projectVersion, String filename )
082    {
083        return new File( basedir, toPath( namespace, projectId, projectVersion, filename ) );
084    }
085
086    @Override
087    public File toFile( File basedir, String namespace, String projectId, String projectVersion )
088    {
089        return new File( basedir, toPath( namespace, projectId, projectVersion ) );
090    }
091
092    @Override
093    public String toPath( String namespace, String projectId, String projectVersion, String filename )
094    {
095        StringBuilder path = new StringBuilder();
096
097        appendNamespaceToProjectVersion( path, namespace, projectId, projectVersion );
098        path.append( PATH_SEPARATOR );
099        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 File toFile( File basedir, String namespace, String projectId )
152    {
153        return new File( basedir, toPath( namespace, projectId ) );
154    }
155
156    @Override
157    public File toFile( File basedir, String namespace )
158    {
159        return new File( basedir, 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}