001    package org.apache.archiva.repository.content.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    
022    import org.apache.archiva.model.ArtifactReference;
023    import org.apache.archiva.repository.ManagedRepositoryContent;
024    import org.apache.archiva.repository.content.PathParser;
025    import org.apache.archiva.repository.content.legacy.LegacyPathParser;
026    import org.apache.archiva.repository.content.legacy.ManagedLegacyRepositoryContent;
027    import org.apache.archiva.repository.layout.LayoutException;
028    import org.apache.archiva.repository.metadata.MetadataTools;
029    import org.apache.commons.lang.StringUtils;
030    
031    /**
032     * RepositoryRequest is used to determine the type of request that is incoming, and convert it to an appropriate
033     * ArtifactReference.
034     * <p/>
035     * <p/>
036     * <p/>
037     */
038    public class RepositoryRequest
039    {
040        private PathParser defaultPathParser = new DefaultPathParser();
041    
042        private PathParser legacyPathParser;
043    
044        public RepositoryRequest( LegacyPathParser legacyPathParser )
045        {
046            this.legacyPathParser = legacyPathParser;
047        }
048    
049        /**
050         * Takes an incoming requested path (in "/" format) and gleans the layout
051         * and ArtifactReference appropriate for that content.
052         *
053         * @param requestedPath the relative path to the content.
054         * @return the ArtifactReference for the requestedPath.
055         * @throws org.apache.archiva.repository.layout.LayoutException if the request path is not layout valid.
056         */
057        public ArtifactReference toArtifactReference( String requestedPath )
058            throws LayoutException
059        {
060            if ( StringUtils.isBlank( requestedPath ) )
061            {
062                throw new LayoutException( "Blank request path is not a valid." );
063            }
064    
065            String path = requestedPath;
066            while ( path.startsWith( "/" ) )
067            {
068                path = path.substring( 1 );
069    
070                // Only slash? that's bad, mmm-kay?
071                if ( "/".equals( path ) )
072                {
073                    throw new LayoutException( "Invalid request path: Slash only." );
074                }
075            }
076    
077            if ( isDefault( path ) )
078            {
079                return defaultPathParser.toArtifactReference( path );
080            }
081            else if ( isLegacy( path ) )
082            {
083                return legacyPathParser.toArtifactReference( path );
084            }
085            else
086            {
087                throw new LayoutException( "Not a valid request path layout, too short." );
088            }
089        }
090    
091        /**
092         * <p>
093         * Tests the path to see if it conforms to the expectations of a metadata request.
094         * </p>
095         * <p>
096         * NOTE: This does a cursory check on the path's last element.  A result of true
097         * from this method is not a guarantee that the metadata is in a valid format, or
098         * that it even contains data.
099         * </p>
100         *
101         * @param requestedPath the path to test.
102         * @return true if the requestedPath is likely a metadata request.
103         */
104        public boolean isMetadata( String requestedPath )
105        {
106            return requestedPath.endsWith( "/" + MetadataTools.MAVEN_METADATA );
107        }
108    
109        /**
110         * @param requestedPath
111         * @return true if the requestedPath is likely an archetype catalog request.
112         */
113        public boolean isArchetypeCatalog( String requestedPath )
114        {
115            return requestedPath.endsWith( "/" + MetadataTools.MAVEN_ARCHETYPE_CATALOG );
116        }
117    
118        /**
119         * <p>
120         * Tests the path to see if it conforms to the expectations of a support file request.
121         * </p>
122         * <p>
123         * Tests for <code>.sha1</code>, <code>.md5</code>, <code>.asc</code>, and <code>.php</code>.
124         * </p>
125         * <p>
126         * NOTE: This does a cursory check on the path's extension only.  A result of true
127         * from this method is not a guarantee that the support resource is in a valid format, or
128         * that it even contains data.
129         * </p>
130         *
131         * @param requestedPath the path to test.
132         * @return true if the requestedPath is likely that of a support file request.
133         */
134        public boolean isSupportFile( String requestedPath )
135        {
136            int idx = requestedPath.lastIndexOf( '.' );
137            if ( idx <= 0 )
138            {
139                return false;
140            }
141    
142            String ext = requestedPath.substring( idx );
143            return ( ".sha1".equals( ext ) || ".md5".equals( ext ) || ".asc".equals( ext ) || ".pgp".equals( ext ) );
144        }
145    
146        public boolean isMetadataSupportFile( String requestedPath )
147        {
148            if ( isSupportFile( requestedPath ) )
149            {
150                String basefilePath = StringUtils.substring( requestedPath, 0, requestedPath.lastIndexOf( '.' ) );
151                if ( isMetadata( basefilePath ) )
152                {
153                    return true;
154                }
155            }
156    
157            return false;
158        }
159    
160        /**
161         * <p>
162         * Tests the path to see if it conforms to the expectations of a default layout request.
163         * </p>
164         * <p>
165         * NOTE: This does a cursory check on the count of path elements only.  A result of
166         * true from this method is not a guarantee that the path sections are valid and
167         * can be resolved to an artifact reference.  use {@link #toArtifactReference(String)}
168         * if you want a more complete analysis of the validity of the path.
169         * </p>
170         *
171         * @param requestedPath the path to test.
172         * @return true if the requestedPath is likely that of a default layout request.
173         */
174        public boolean isDefault( String requestedPath )
175        {
176            if ( StringUtils.isBlank( requestedPath ) )
177            {
178                return false;
179            }
180    
181            String pathParts[] = StringUtils.splitPreserveAllTokens( requestedPath, '/' );
182            if ( pathParts.length > 3 )
183            {
184                return true;
185            }
186            else if ( pathParts.length == 3 )
187            {
188                // check if artifact-level metadata (ex. eclipse/jdtcore/maven-metadata.xml)
189                if ( isMetadata( requestedPath ) )
190                {
191                    return true;
192                }
193                else
194                {
195                    // check if checksum of artifact-level metadata (ex. eclipse/jdtcore/maven-metadata.xml.sha1)
196                    int idx = requestedPath.lastIndexOf( '.' );
197                    if ( idx > 0 )
198                    {
199                        String base = requestedPath.substring( 0, idx );
200                        if ( isMetadata( base ) && isSupportFile( requestedPath ) )
201                        {
202                            return true;
203                        }
204                    }
205    
206                    return false;
207                }
208            }
209            else
210            {
211                return false;
212            }
213        }
214    
215        /**
216         * <p>
217         * Tests the path to see if it conforms to the expectations of a legacy layout request.
218         * </p>
219         * <p>
220         * NOTE: This does a cursory check on the count of path elements only.  A result of
221         * true from this method is not a guarantee that the path sections are valid and
222         * can be resolved to an artifact reference.  use {@link #toArtifactReference(String)}
223         * if you want a more complete analysis of the validity of the path.
224         * </p>
225         *
226         * @param requestedPath the path to test.
227         * @return true if the requestedPath is likely that of a legacy layout request.
228         */
229        public boolean isLegacy( String requestedPath )
230        {
231            if ( StringUtils.isBlank( requestedPath ) )
232            {
233                return false;
234            }
235    
236            String pathParts[] = StringUtils.splitPreserveAllTokens( requestedPath, '/' );
237            return pathParts.length == 3;
238        }
239    
240        /**
241         * Adjust the requestedPath to conform to the native layout of the provided {@link org.apache.archiva.repository.ManagedRepositoryContent}.
242         *
243         * @param requestedPath the incoming requested path.
244         * @param repository    the repository to adjust to.
245         * @return the adjusted (to native) path.
246         * @throws org.apache.archiva.repository.layout.LayoutException if the path cannot be parsed.
247         */
248        public String toNativePath( String requestedPath, ManagedRepositoryContent repository )
249            throws LayoutException
250        {
251            if ( StringUtils.isBlank( requestedPath ) )
252            {
253                throw new LayoutException( "Request Path is blank." );
254            }
255    
256            String referencedResource = requestedPath;
257            // No checksum by default.
258            String supportfile = "";
259    
260            // Figure out support file, and actual referencedResource.
261            if ( isSupportFile( requestedPath ) )
262            {
263                int idx = requestedPath.lastIndexOf( '.' );
264                referencedResource = requestedPath.substring( 0, idx );
265                supportfile = requestedPath.substring( idx );
266            }
267    
268            if ( isMetadata( referencedResource ) )
269            {
270                if ( repository instanceof ManagedLegacyRepositoryContent )
271                {
272                    throw new LayoutException( "Cannot translate metadata request to legacy layout." );
273                }
274    
275                /* Nothing to translate.
276                 * Default layout is the only layout that can contain maven-metadata.xml files, and
277                 * if the managedRepository is layout legacy, this request would never occur.
278                 */
279                return requestedPath;
280            }
281    
282            // Treat as an artifact reference.
283            ArtifactReference ref = toArtifactReference( referencedResource );
284            String adjustedPath = repository.toPath( ref );
285            return adjustedPath + supportfile;
286        }
287    }