]> source.dussan.org Git - archiva.git/blob
0b25b0cb37a33b570fec75046611cf6c31a4367f
[archiva.git] /
1 package org.apache.maven.archiva.repository.layout;
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.commons.lang.StringUtils;
23 import org.apache.maven.archiva.common.utils.VersionUtil;
24 import org.apache.maven.archiva.model.ArchivaArtifact;
25 import org.apache.maven.archiva.model.ArtifactReference;
26 import org.apache.maven.archiva.repository.content.ArtifactExtensionMapping;
27
28 /**
29  * DefaultBidirectionalRepositoryLayout - the layout mechanism for use by Maven 2.x repositories.
30  *
31  * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
32  * @version $Id$
33  * @plexus.component role-hint="default"
34  */
35 public class DefaultBidirectionalRepositoryLayout
36     implements BidirectionalRepositoryLayout
37 {
38     class PathReferences
39     {
40         public String groupId;
41
42         public String artifactId;
43
44         public String baseVersion;
45
46         public String type;
47
48         public FilenameParts fileParts;
49
50         public void appendGroupId( String part )
51         {
52             if ( groupId == null )
53             {
54                 groupId = part;
55                 return;
56             }
57
58             groupId += "." + part;
59         }
60     }
61
62     private static final char PATH_SEPARATOR = '/';
63
64     private static final char GROUP_SEPARATOR = '.';
65
66     private static final char ARTIFACT_SEPARATOR = '-';
67
68     public String getId()
69     {
70         return "default";
71     }
72
73     public ArchivaArtifact toArtifact( String path )
74         throws LayoutException
75     {
76         PathReferences pathrefs = toPathReferences( path );
77
78         ArchivaArtifact artifact = new ArchivaArtifact( pathrefs.groupId, pathrefs.artifactId,
79                                                         pathrefs.fileParts.version, pathrefs.fileParts.classifier,
80                                                         pathrefs.type );
81
82         return artifact;
83     }
84
85     public ArtifactReference toArtifactReference( String path )
86         throws LayoutException
87     {
88         PathReferences pathrefs = toPathReferences( path );
89
90         ArtifactReference reference = new ArtifactReference();
91         reference.setGroupId( pathrefs.groupId );
92         reference.setArtifactId( pathrefs.artifactId );
93         reference.setVersion( pathrefs.fileParts.version );
94         reference.setClassifier( pathrefs.fileParts.classifier );
95         reference.setType( pathrefs.type );
96
97         return reference;
98     }
99
100     public String toPath( ArchivaArtifact artifact )
101     {
102         if ( artifact == null )
103         {
104             throw new IllegalArgumentException( "Artifact cannot be null" );
105         }
106
107         return toPath( artifact.getGroupId(), artifact.getArtifactId(), artifact.getBaseVersion(), artifact
108             .getVersion(), artifact.getClassifier(), artifact.getType() );
109     }
110
111     public String toPath( ArtifactReference reference )
112     {
113         if ( reference == null )
114         {
115             throw new IllegalArgumentException( "Artifact reference cannot be null" );
116         }
117
118         String baseVersion = VersionUtil.getBaseVersion( reference.getVersion() );
119         return toPath( reference.getGroupId(), reference.getArtifactId(), baseVersion, reference.getVersion(),
120                        reference.getClassifier(), reference.getType() );
121     }
122
123     private String formatAsDirectory( String directory )
124     {
125         return directory.replace( GROUP_SEPARATOR, PATH_SEPARATOR );
126     }
127
128     private String toPath( String groupId, String artifactId, String baseVersion, String version, String classifier,
129                            String type )
130     {
131         StringBuffer path = new StringBuffer();
132
133         path.append( formatAsDirectory( groupId ) ).append( PATH_SEPARATOR );
134         path.append( artifactId ).append( PATH_SEPARATOR );
135
136         if ( baseVersion != null )
137         {
138             path.append( baseVersion ).append( PATH_SEPARATOR );
139             if ( ( version != null ) && ( type != null ) )
140             {
141                 path.append( artifactId ).append( ARTIFACT_SEPARATOR ).append( version );
142
143                 if ( StringUtils.isNotBlank( classifier ) )
144                 {
145                     path.append( ARTIFACT_SEPARATOR ).append( classifier );
146                 }
147
148                 path.append( GROUP_SEPARATOR ).append( ArtifactExtensionMapping.getExtension( type ) );
149             }
150         }
151
152         return path.toString();
153     }
154
155     public boolean isValidPath( String path )
156     {
157         try
158         {
159             toPathReferences( path );
160             return true;
161         }
162         catch ( LayoutException e )
163         {
164             return false;
165         }
166     }
167
168     private PathReferences toPathReferences( String path )
169         throws LayoutException
170     {
171         if ( StringUtils.isBlank( path ) )
172         {
173             throw new LayoutException( "Unable to convert blank path." );
174         }
175
176         PathReferences prefs = new PathReferences();
177
178         String normalizedPath = StringUtils.replace( path, "\\", "/" );
179         String pathParts[] = StringUtils.split( normalizedPath, '/' );
180
181         /* Minimum parts.
182          *
183          *   path = "commons-lang/commons-lang/2.1/commons-lang-2.1.jar"
184          *   path[0] = "commons-lang";        // The Group ID
185          *   path[1] = "commons-lang";        // The Artifact ID
186          *   path[2] = "2.1";                 // The Version
187          *   path[3] = "commons-lang-2.1.jar" // The filename.
188          */
189
190         if ( pathParts.length < 4 )
191         {
192             // Illegal Path Parts Length.
193             throw new LayoutException( "Not enough parts to the path [" + path
194                 + "] to construct an ArchivaArtifact from. (Requires at least 4 parts)" );
195         }
196
197         // Maven 2.x path.
198         int partCount = pathParts.length;
199         int filenamePos = partCount - 1;
200         int baseVersionPos = partCount - 2;
201         int artifactIdPos = partCount - 3;
202         int groupIdPos = partCount - 4;
203
204         // Second to last is the baseVersion (the directory version)
205         prefs.baseVersion = pathParts[baseVersionPos];
206
207         // Third to last is the artifact Id.
208         prefs.artifactId = pathParts[artifactIdPos];
209
210         // Remaining pieces are the groupId.
211         for ( int i = 0; i <= groupIdPos; i++ )
212         {
213             prefs.appendGroupId( pathParts[i] );
214         }
215
216         try
217         {
218             // Last part is the filename
219             String filename = pathParts[filenamePos];
220
221             // Now we need to parse the filename to get the artifact version Id.
222             prefs.fileParts = RepositoryLayoutUtils.splitFilename( filename, prefs.artifactId, prefs.baseVersion );
223
224             /* If classifier is discovered, see if it deserves to be.
225              *
226              * Filenames like "comm-3.0-u1.jar" might be identified as having a version of "3.0"
227              * and a classifier of "u1".
228              *
229              * This routine will take the version + classifier and compare it to the prefs.baseVersion and
230              * move the classifierensure that
231              *
232              * javax/comm/3.0-u1/comm-3.0-u1.jar
233              */
234             if ( StringUtils.isNotBlank( prefs.fileParts.classifier ) )
235             {
236                 String conjoinedVersion = prefs.fileParts.version + "-" + prefs.fileParts.classifier;
237
238                 if( StringUtils.equals( prefs.baseVersion, conjoinedVersion ) )
239                 {
240                     prefs.fileParts.version = conjoinedVersion;
241                     prefs.fileParts.classifier = null;
242                 }
243             }
244
245             prefs.type = ArtifactExtensionMapping.guessTypeFromFilename( filename );
246         }
247         catch ( LayoutException e )
248         {
249             throw e;
250         }
251
252         // Sanity Checks.
253         if ( prefs.fileParts != null )
254         {
255             /* Compare artifact version to path baseversion.
256              *
257              * Version naming in the wild can be strange at times.
258              * Sometimes what is seen as a classifier is actually part of the version id.
259              *
260              * To compensate for this, the path is checked against the artifact.version and
261              *  the concatenation of the artifact.version + "-" + artifact.classifier
262              */
263             String pathVersion = prefs.baseVersion;
264             String artifactVersion = prefs.fileParts.version;
265
266             // Do we have a snapshot version?
267             if ( VersionUtil.isSnapshot( artifactVersion ) )
268             {
269                 // Rules are different for SNAPSHOTS
270                 if ( !VersionUtil.isGenericSnapshot( pathVersion ) )
271                 {
272                     String baseVersion = VersionUtil.getBaseVersion( prefs.fileParts.version );
273                     throw new LayoutException( "Invalid snapshot artifact location, version directory should be "
274                         + baseVersion );
275                 }
276             }
277             else
278             {
279                 // Non SNAPSHOT rules.
280                 // Do we pass the simple test?
281                 if ( !StringUtils.equals( pathVersion, artifactVersion ) )
282                 {
283                     // Do we have a classifier?  If so, test the conjoined case.
284                     if ( StringUtils.isNotBlank( prefs.fileParts.classifier ) )
285                     {
286                         String artifactLongVersion = artifactVersion + "-" + prefs.fileParts.classifier;
287                         if ( !StringUtils.equals( pathVersion, artifactLongVersion ) )
288                         {
289                             throw new LayoutException( "Invalid artifact: version declared in directory path does"
290                                 + " not match what was found in the artifact filename." );
291                         }
292                     }
293                     else
294                     {
295                         throw new LayoutException( "Invalid artifact: version declared in directory path does"
296                             + " not match what was found in the artifact filename." );
297                     }
298                 }
299             }
300
301             // Test if the artifactId present on the directory path is the same as the artifactId filename.
302             if ( !prefs.artifactId.equals( prefs.fileParts.artifactId ) )
303             {
304                 throw new LayoutException( "Invalid artifact Id" );
305             }
306         }
307
308         return prefs;
309     }
310 }