]> source.dussan.org Git - archiva.git/blob
6970d0eb5674f533a58d3999deba48ba587028b2
[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.filelock.FileLockManager;
22 import org.apache.archiva.common.utils.FileUtils;
23 import org.apache.archiva.common.utils.VersionUtil;
24 import org.apache.archiva.configuration.FileTypes;
25 import org.apache.archiva.metadata.maven.MavenMetadataReader;
26 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
27 import org.apache.archiva.repository.maven.metadata.storage.ArtifactMappingProvider;
28 import org.apache.archiva.repository.maven.metadata.storage.DefaultArtifactMappingProvider;
29 import org.apache.archiva.model.ArchivaArtifact;
30 import org.apache.archiva.model.ArtifactReference;
31 import org.apache.archiva.model.ProjectReference;
32 import org.apache.archiva.model.VersionedReference;
33 import org.apache.archiva.repository.ContentAccessException;
34 import org.apache.archiva.repository.ContentNotFoundException;
35 import org.apache.archiva.repository.EditableManagedRepository;
36 import org.apache.archiva.repository.LayoutException;
37 import org.apache.archiva.repository.ManagedRepository;
38 import org.apache.archiva.repository.ManagedRepositoryContent;
39 import org.apache.archiva.repository.content.Artifact;
40 import org.apache.archiva.repository.content.ContentItem;
41 import org.apache.archiva.repository.content.ItemNotFoundException;
42 import org.apache.archiva.repository.content.ItemSelector;
43 import org.apache.archiva.repository.content.Namespace;
44 import org.apache.archiva.repository.content.Project;
45 import org.apache.archiva.repository.content.Version;
46 import org.apache.archiva.repository.content.base.ArchivaNamespace;
47 import org.apache.archiva.repository.content.base.ArchivaProject;
48 import org.apache.archiva.repository.content.base.ArchivaVersion;
49 import org.apache.archiva.repository.content.base.builder.ArtifactOptBuilder;
50 import org.apache.archiva.repository.storage.RepositoryStorage;
51 import org.apache.archiva.repository.storage.StorageAsset;
52 import org.apache.archiva.repository.storage.util.StorageUtil;
53 import org.apache.commons.collections4.map.ReferenceMap;
54 import org.apache.commons.lang3.StringUtils;
55
56 import javax.inject.Inject;
57 import javax.inject.Named;
58 import java.io.IOException;
59 import java.net.URI;
60 import java.nio.file.Files;
61 import java.nio.file.Path;
62 import java.nio.file.Paths;
63 import java.util.Collections;
64 import java.util.List;
65 import java.util.Objects;
66 import java.util.Optional;
67 import java.util.Set;
68 import java.util.function.Predicate;
69 import java.util.regex.Matcher;
70 import java.util.regex.Pattern;
71 import java.util.stream.Collectors;
72 import java.util.stream.Stream;
73
74 /**
75  * ManagedDefaultRepositoryContent
76  */
77 public class ManagedDefaultRepositoryContent
78     extends AbstractDefaultRepositoryContent
79     implements ManagedRepositoryContent
80 {
81
82     public  static final String METADATA_FILENAME = "maven-metadata.xml";
83     private FileTypes filetypes;
84
85     public void setFileTypes(FileTypes fileTypes) {
86         this.filetypes = fileTypes;
87     }
88
89     private ManagedRepository repository;
90
91     private FileLockManager lockManager;
92
93     @Inject
94     @Named("repositoryPathTranslator#maven2")
95     private RepositoryPathTranslator pathTranslator;
96
97     @Inject
98     @Named( "metadataReader#maven" )
99     MavenMetadataReader metadataReader;
100
101     @Inject
102     @Named( "MavenContentHelper" )
103     MavenContentHelper mavenContentHelper;
104
105     public static final String SNAPSHOT = "SNAPSHOT";
106
107     public static final Pattern UNIQUE_SNAPSHOT_PATTERN = Pattern.compile( "^(SNAPSHOT|[0-9]{8}\\.[0-9]{6}-[0-9]+)(.*)" );
108     public static final Pattern CLASSIFIER_PATTERN = Pattern.compile( "^-([^.]+)(\\..*)" );
109
110     public static final Pattern TIMESTAMP_PATTERN = Pattern.compile( "^([0-9]{8})\\.([0-9]{6})$" );
111
112     public static final Pattern GENERIC_SNAPSHOT_PATTERN = Pattern.compile( "^(.*)-" + SNAPSHOT );
113
114     /**
115      * We are caching content items in a weak reference map. To avoid always recreating the
116      * the hierarchical structure.
117      * TODO: Better use a object cache? E.g. our spring cache implementation?
118      */
119     private ReferenceMap<String, Namespace> namespaceMap = new ReferenceMap<>( );
120     private ReferenceMap<StorageAsset, Project> projectMap = new ReferenceMap<>( );
121     private ReferenceMap<StorageAsset, Version> versionMap = new ReferenceMap<>( );
122     private ReferenceMap<StorageAsset, Artifact> artifactMap = new ReferenceMap<>( );
123
124     public ManagedDefaultRepositoryContent() {
125         super(Collections.singletonList( new DefaultArtifactMappingProvider() ));
126     }
127
128     public ManagedDefaultRepositoryContent(ManagedRepository repository, FileTypes fileTypes, FileLockManager lockManager) {
129         super(Collections.singletonList( new DefaultArtifactMappingProvider() ));
130         setFileTypes( fileTypes );
131         this.lockManager = lockManager;
132         setRepository( repository );
133     }
134
135     public ManagedDefaultRepositoryContent( ManagedRepository repository, List<? extends ArtifactMappingProvider> artifactMappingProviders, FileTypes fileTypes, FileLockManager lockManager )
136     {
137         super(artifactMappingProviders==null ? Collections.singletonList( new DefaultArtifactMappingProvider() ) : artifactMappingProviders);
138         setFileTypes( fileTypes );
139         this.lockManager = lockManager;
140         setRepository( repository );
141
142     }
143
144     private StorageAsset getAssetByPath(String assetPath) {
145         return getStorage( ).getAsset( assetPath );
146     }
147
148     private StorageAsset getAsset(String namespace) {
149         String namespacePath = formatAsDirectory( namespace.trim() );
150         if (StringUtils.isEmpty( namespacePath )) {
151             namespacePath = "";
152         }
153         return getAssetByPath(namespacePath);
154     }
155
156     private StorageAsset getAsset(String namespace, String project) {
157         return getAsset( namespace ).resolve( project );
158     }
159
160     private StorageAsset getAsset(String namespace, String project, String version) {
161         return getAsset( namespace, project ).resolve( version );
162     }
163
164     private StorageAsset getAsset(String namespace, String project, String version, String fileName) {
165         return getAsset( namespace, project, version ).resolve( fileName );
166     }
167
168
169     /// ************* End of new generation interface ******************
170     @Override
171     public void deleteItem( ContentItem item ) throws ItemNotFoundException, ContentAccessException
172     {
173         final Path baseDirectory = getRepoDir( );
174         final Path itemPath = item.getAsset( ).getFilePath( );
175         if ( !Files.exists( itemPath ) )
176         {
177             throw new ItemNotFoundException( "The item " + item.toString() + "does not exist in the repository " + getId( ) );
178         }
179         if ( !itemPath.toAbsolutePath().startsWith( baseDirectory.toAbsolutePath() ) )
180         {
181             log.error( "The namespace {} to delete from repository {} is not a subdirectory of the repository base.", item, getId( ) );
182             log.error( "Namespace directory: {}", itemPath );
183             log.error( "Repository directory: {}", baseDirectory );
184             throw new ContentAccessException( "Inconsistent directories found. Could not delete namespace." );
185         }
186         try
187         {
188             if (Files.isDirectory( itemPath ))
189             {
190                 FileUtils.deleteDirectory( itemPath );
191             } else {
192                 Files.deleteIfExists( itemPath );
193             }
194         }
195         catch ( IOException e )
196         {
197             log.error( "Could not delete namespace directory {}: {}", itemPath, e.getMessage( ), e );
198             throw new ContentAccessException( "Error occured while deleting namespace " + item + ": " + e.getMessage( ), e );
199         }
200     }
201
202     @Override
203     public ContentItem getItem( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
204     {
205         if (selector.hasVersion() && selector.hasArtifactId()) {
206             return getArtifact( selector );
207         } else if (selector.hasProjectId() && selector.hasVersion()) {
208             return getVersion( selector );
209         } else if (selector.hasProjectId()) {
210             return getProject( selector );
211         } else {
212             return getNamespace( selector );
213         }
214     }
215
216     @Override
217     public Namespace getNamespace( final ItemSelector namespaceSelector ) throws ContentAccessException, IllegalArgumentException
218     {
219         return namespaceMap.computeIfAbsent( namespaceSelector.getNamespace(),
220             namespace -> {
221                 StorageAsset nsPath = getAsset( namespace );
222                 return ArchivaNamespace.withRepository( this ).withAsset( nsPath ).
223                     withNamespace( namespace ).build( );
224             });
225     }
226
227
228     @Override
229     public Project getProject( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
230     {
231         if (!selector.hasProjectId()) {
232             throw new IllegalArgumentException( "Project id must be set" );
233         }
234         final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ) );
235         return projectMap.computeIfAbsent( path, projectPath -> {
236             final Namespace ns = getNamespace( selector );
237             return ArchivaProject.withAsset( projectPath ).withNamespace( ns ).withId( selector.getProjectId( ) ).build( );
238         }
239         );
240     }
241
242
243     @Override
244     public Version getVersion( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
245     {
246         if (!selector.hasProjectId()) {
247             throw new IllegalArgumentException( "Project id must be set" );
248         }
249         if (!selector.hasVersion() ) {
250             throw new IllegalArgumentException( "Version must be set" );
251         }
252         final StorageAsset path = getAsset(selector.getNamespace(), selector.getProjectId(), selector.getVersion());
253         return versionMap.computeIfAbsent( path, versionPath -> {
254             final Project project = getProject( selector );
255             return ArchivaVersion.withAsset( path )
256                 .withProject( project )
257                 .withVersion( selector.getVersion( ) ).build();
258         } );
259     }
260
261
262
263
264
265     public Artifact createArtifact(final StorageAsset artifactPath, final ItemSelector selector,
266         final String classifier, final String extension) {
267         Version version = getVersion(selector);
268         ArtifactOptBuilder builder = org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
269             .withVersion( version )
270             .withId( selector.getArtifactId( ) )
271             .withArtifactVersion( mavenContentHelper.getArtifactVersion( artifactPath, selector ) )
272             .withClassifier( classifier );
273         if (selector.hasType()) {
274             builder.withType( selector.getType( ) );
275         }
276         return builder.build( );
277     }
278
279     public Namespace getNamespaceFromArtifactPath( final StorageAsset artifactPath) {
280         final StorageAsset namespacePath = artifactPath.getParent( ).getParent( ).getParent( );
281         final String namespace = MavenContentHelper.getNamespaceFromNamespacePath( namespacePath );
282         return namespaceMap.computeIfAbsent( namespace,
283             myNamespace -> ArchivaNamespace.withRepository( this )
284                 .withAsset( namespacePath )
285                 .withNamespace( namespace )
286                 .build( ) );
287     }
288
289     private Project getProjectFromArtifactPath( final StorageAsset artifactPath) {
290         final StorageAsset projectPath = artifactPath.getParent( ).getParent( );
291         return projectMap.computeIfAbsent( projectPath,
292             myProjectPath -> ArchivaProject.withAsset( projectPath )
293                 .withNamespace( getNamespaceFromArtifactPath( artifactPath ) )
294                 .withId( projectPath.getName( ) ).build( )
295         );
296     }
297
298     private Version getVersionFromArtifactPath( final StorageAsset artifactPath) {
299         final StorageAsset versionPath = artifactPath.getParent( );
300         return versionMap.computeIfAbsent( versionPath,
301             myVersionPath -> ArchivaVersion.withAsset( versionPath )
302                 .withProject( getProjectFromArtifactPath( artifactPath ) )
303                 .withVersion( versionPath.getName( ) ).build( ) );
304     }
305
306     private Artifact getArtifactFromPath(final StorageAsset artifactPath) {
307         final Version version = getVersionFromArtifactPath( artifactPath );
308         final ArtifactInfo info  = getArtifactInfoFromPath( version.getVersion(), artifactPath );
309         return artifactMap.computeIfAbsent( artifactPath, myArtifactPath ->
310             org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
311                 .withVersion( version )
312                 .withId( info.id )
313                 .withClassifier( info.classifier )
314                 .withRemainder( info.remainder )
315                 .withType( info.type )
316                 .withArtifactVersion( info.version )
317                 .withContentType( info.contentType )
318                 .build( )
319         );
320     }
321
322     private ContentItem getItemFromPath(final StorageAsset itemPath) {
323         if (itemPath.isLeaf()) {
324             return getArtifactFromPath( itemPath );
325         } else {
326             if (versionMap.containsKey( itemPath )) {
327                 return versionMap.get( itemPath );
328             }
329             if (projectMap.containsKey( itemPath )) {
330                 return projectMap.get( itemPath );
331             }
332             String ns = MavenContentHelper.getNamespaceFromNamespacePath( itemPath );
333             if (namespaceMap.containsKey( ns )) {
334                 return namespaceMap.get( ns );
335             }
336             // No cached item, so we have to gather more information:
337             // Check for version directory (contains at least a pom or metadata file)
338             if (itemPath.list( ).stream( ).map(a -> a.getName().toLowerCase()).anyMatch( n ->
339                 n.endsWith( ".pom" )
340                 || n.startsWith( "maven-metadata" )
341             )) {
342                 return versionMap.computeIfAbsent( itemPath,
343                     myVersionPath -> ArchivaVersion.withAsset( itemPath )
344                         .withProject( (Project)getItemFromPath( itemPath.getParent() ) )
345                         .withVersion( itemPath.getName() ).build());
346             } else {
347                 // We have to dig further and find the next directory with a pom
348                 Optional<StorageAsset> foundFile = StorageUtil.newAssetStream( itemPath )
349                     .filter( a -> a.getName().toLowerCase().endsWith( ".pom" )
350                         || a.getName().toLowerCase().startsWith( "maven-metadata" ) )
351                     .findFirst( );
352                 if (foundFile.isPresent())
353                 {
354                     int level = 0;
355                     StorageAsset current = foundFile.get( );
356                     while (current.hasParent() && !current.equals(itemPath)) {
357                         level++;
358                         current = current.getParent( );
359                     }
360                     // Project path if it is one level up from the found file
361                     if (level==2) {
362                         return projectMap.computeIfAbsent( itemPath,
363                             myItemPath -> getProjectFromArtifactPath( foundFile.get( ) ) );
364                     } else {
365                         // All other paths are treated as namespace
366                         return namespaceMap.computeIfAbsent( ns,
367                             myNamespace -> ArchivaNamespace.withRepository( this )
368                                 .withAsset( itemPath )
369                                 .withNamespace( ns )
370                                 .build( ) );
371                     }
372                 } else {
373                     // Don't know what to do with it, so we treat it as namespace path
374                     return namespaceMap.computeIfAbsent( ns,
375                         myNamespace -> ArchivaNamespace.withRepository( this )
376                             .withAsset( itemPath )
377                             .withNamespace( ns )
378                             .build( ) );
379                 }
380
381             }
382         }
383     }
384
385     // Simple object to hold artifact information
386     private class ArtifactInfo  {
387         private String id;
388         private String version;
389         private String extension;
390         private String remainder;
391         private String type;
392         private String classifier;
393         private String contentType;
394     }
395
396     private ArtifactInfo getArtifactInfoFromPath(String genericVersion, StorageAsset path) {
397         final ArtifactInfo info = new ArtifactInfo( );
398         info.id = path.getParent( ).getParent( ).getName( );
399         final String fileName = path.getName( );
400         if ( genericVersion.endsWith( "-" + SNAPSHOT ) )
401         {
402             String baseVersion = StringUtils.substringBeforeLast( genericVersion, "-" + SNAPSHOT );
403             String prefix = info.id+"-"+baseVersion+"-";
404             if (fileName.startsWith( prefix ))
405             {
406                 String versionPostfix = StringUtils.removeStart( fileName, prefix );
407                 Matcher matcher = UNIQUE_SNAPSHOT_PATTERN.matcher( versionPostfix );
408                 if (matcher.matches()) {
409                     info.version = baseVersion + "-" + matcher.group( 1 );
410                     String newPrefix = prefix + info.version;
411                     if (fileName.startsWith( newPrefix ))
412                     {
413                         String classPostfix = StringUtils.removeStart( fileName, newPrefix );
414                         Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
415                         if (cMatch.matches()) {
416                             info.classifier = cMatch.group( 1 );
417                             info.remainder = cMatch.group( 2 );
418                         } else {
419                             info.classifier = "";
420                             info.remainder = classPostfix;
421                         }
422                     } else {
423                         log.error( "Artifact does not match the maven name pattern {}", path );
424                         info.classifier = "";
425                         info.remainder = StringUtils.substringAfter( fileName, prefix );
426                     }
427                 } else {
428                     log.error( "Artifact does not match the snapshot version pattern {}", path );
429                     info.version = "";
430                     info.classifier = "";
431                     info.remainder = StringUtils.substringAfter( fileName, prefix );
432                 }
433             } else {
434                 log.error( "Artifact does not match the maven name pattern: {}", path );
435                 info.version = "";
436                 info.classifier = "";
437                 info.remainder = StringUtils.substringAfterLast( fileName, "." );
438             }
439         } else {
440             String prefix = info.id+"-"+genericVersion;
441             if (fileName.startsWith( prefix ))
442             {
443                 info.version=genericVersion;
444                 String classPostfix = StringUtils.removeStart( fileName, prefix );
445                 Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
446                 if (cMatch.matches()) {
447                     info.classifier = cMatch.group( 1 );
448                     info.remainder = cMatch.group( 2 );
449                 } else {
450                     info.classifier = "";
451                     info.remainder = classPostfix;
452                 }
453             } else {
454                 log.error( "Artifact does not match the version pattern {}", path );
455                 info.version = "";
456                 info.classifier = "";
457                 info.remainder = StringUtils.substringAfterLast( fileName, "." );
458             }
459         }
460         info.extension = StringUtils.substringAfterLast( fileName, "." );
461         info.type = MavenContentHelper.getTypeFromClassifierAndExtension( info.classifier, info.extension );
462         try {
463             info.contentType = Files.probeContentType( path.getFilePath( ) );
464         } catch (IOException e) {
465             info.contentType = "";
466             //
467         }
468         return info;
469
470     }
471
472     @Override
473     public Artifact getArtifact( final ItemSelector selector ) throws ContentAccessException
474     {
475         if (!selector.hasProjectId( )) {
476             throw new IllegalArgumentException( "Project id must be set" );
477         }
478         if (!selector.hasVersion( )) {
479             throw new IllegalArgumentException( "Version must be set" );
480         }
481         if (!selector.hasArtifactId( )) {
482             throw new IllegalArgumentException( "Artifact Id must be set" );
483         }
484         final StorageAsset artifactDir = getAsset(selector.getNamespace(), selector.getProjectId(),
485             selector.getVersion());
486         final String artifactVersion = mavenContentHelper.getArtifactVersion( artifactDir, selector );
487         final String classifier = MavenContentHelper.getClassifier( selector );
488         final String extension = MavenContentHelper.getArtifactExtension( selector );
489         final String artifactId = StringUtils.isEmpty( selector.getArtifactId( ) ) ? selector.getProjectId( ) : selector.getArtifactId( );
490         final String fileName = MavenContentHelper.getArtifactFileName( artifactId, artifactVersion, classifier, extension );
491         final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ),
492             selector.getVersion( ), fileName );
493         return artifactMap.computeIfAbsent( path, artifactPath -> createArtifact( path, selector, classifier, extension ) );
494     }
495
496     private StorageAsset getBasePathFromSelector(ItemSelector selector) {
497         StringBuilder path = new StringBuilder( );
498         if (selector.hasNamespace()) {
499             path.append(String.join( "/", getNamespace( selector ).getNamespacePath( ) ));
500         }
501         if (selector.hasProjectId()) {
502             path.append( "/" ).append( selector.getProjectId( ) );
503         }
504         if (selector.hasVersion()) {
505             path.append( "/" ).append( selector.getVersion( ) );
506         }
507         return getStorage( ).getAsset( path.toString( ) );
508     }
509
510     /*
511      * File filter to select certain artifacts using the selector data.
512      */
513     private Predicate<StorageAsset> getFileFilterFromSelector(final ItemSelector selector) {
514         Predicate<StorageAsset> p = a -> a.isLeaf( );
515         if (selector.hasArtifactId()) {
516             final String pattern = selector.getArtifactId( );
517             p = p.and( a -> StringUtils.startsWithIgnoreCase( a.getName( ),  pattern ) );
518         }
519         if (selector.hasArtifactVersion()) {
520             final String pattern = selector.getArtifactVersion( );
521             p = p.and( a -> StringUtils.containsIgnoreCase( a.getName( ),  pattern ) );
522         }
523         if (selector.hasExtension()) {
524             final String pattern = "."+selector.getExtension( );
525             p = p.and( a -> StringUtils.endsWithIgnoreCase( a.getName( ), pattern ) );
526         } else if (selector.hasType()) {
527             final String pattern = "."+ MavenContentHelper.getArtifactExtension( selector );
528             p = p.and( a -> StringUtils.endsWithIgnoreCase( a.getName( ), pattern ) );
529         }
530         if (selector.hasClassifier()) {
531             final String pattern = "-" + selector.getClassifier( ) + ".";
532             p = p.and( a -> StringUtils.containsIgnoreCase( a.getName( ), pattern ) );
533         } else if (selector.hasType()) {
534             final String pattern = "-" + MavenContentHelper.getClassifierFromType( selector.getType( ) ) + ".";
535             p = p.and( a -> StringUtils.containsIgnoreCase( a.getName( ).toLowerCase( ), pattern ) );
536         }
537         return p;
538     }
539
540     /*
541         TBD
542      */
543     @Override
544     public List<? extends Artifact> getAllArtifacts( ItemSelector selector ) throws ContentAccessException
545     {
546         return null;
547     }
548
549     /*
550         TBD
551      */
552     @Override
553     public Stream<? extends Artifact> getAllArtifactStream( ItemSelector selector ) throws ContentAccessException
554     {
555         return null;
556     }
557
558     /*
559         TBD
560      */
561     @Override
562     public List<? extends Project> getProjects( Namespace namespace )
563     {
564         return null;
565     }
566
567     /*
568         TBD
569      */
570     @Override
571     public List<? extends Version> getVersions( final Project project )
572     {
573         StorageAsset asset = getAsset( project.getNamespace( ).getNamespace( ), project.getId( ) );
574         return asset.list( ).stream( ).filter( a -> a.isContainer( ) )
575             .map( a -> ArchivaVersion.withAsset( a )
576                 .withProject( project )
577                 .withVersion( a.getName() ).build() )
578             .collect( Collectors.toList( ) );
579     }
580
581     /*
582         TBD
583      */
584     @Override
585     public List<? extends Artifact> getArtifacts( ContentItem item )
586     {
587         return null;
588     }
589
590     /*
591         TBD
592      */
593     @Override
594     public List<? extends Artifact> getArtifacts( Namespace namespace, boolean recurse )
595     {
596         return null;
597     }
598
599     /*
600         TBD
601      */
602     @Override
603     public Stream<? extends Artifact> getArtifactStream( ContentItem item )
604     {
605         return null;
606     }
607
608     /*
609         TBD
610      */
611     @Override
612     public Stream<? extends Artifact> getArtifactStream( Namespace namespace, boolean recurse )
613     {
614         return null;
615     }
616
617     /*
618         TBD
619      */
620     @Override
621     public boolean hasContent( ItemSelector selector )
622     {
623         return false;
624     }
625
626     /*
627         TBD
628      */
629     @Override
630     public void copyArtifact( Path sourceFile, ContentItem destination ) throws IllegalArgumentException
631     {
632
633     }
634
635     /**
636      * TBD
637      * @param path the path string that points to the item
638      * @return
639      * @throws LayoutException
640      */
641     @Override
642     public ContentItem toItem( String path ) throws LayoutException
643     {
644         ItemSelector selector = getPathParser( ).toItemSelector( path );
645         return getItem( selector );
646     }
647
648     @Override
649     public ContentItem toItem( StorageAsset assetPath ) throws LayoutException
650     {
651         return toItem( assetPath.getPath( ) );
652     }
653
654     /// ************* End of new generation interface ******************
655
656     /**
657      * Returns a version reference from the coordinates
658      * @param groupId the group id
659      * @param artifactId the artifact id
660      * @param version the version
661      * @return the versioned reference object
662      */
663     @Override
664     public VersionedReference toVersion( String groupId, String artifactId, String version ) {
665         return new VersionedReference().groupId( groupId ).artifactId( artifactId ).version( version );
666     }
667
668     @Override
669     public VersionedReference toGenericVersion( ArtifactReference artifactReference )
670     {
671         return toVersion( artifactReference.getGroupId( ), artifactReference.getArtifactId( ), VersionUtil.getBaseVersion( artifactReference.getVersion( ) ));
672     }
673
674     /**
675      * Return the version the artifact is part of
676      * @param artifactReference
677      * @return
678      */
679     public VersionedReference toVersion( ArtifactReference artifactReference) {
680         return toVersion( artifactReference.getGroupId( ), artifactReference.getArtifactId( ), artifactReference.getVersion( ) );
681     }
682
683     @Override
684     public ArtifactReference toArtifact( String groupId, String artifactId, String version, String type, String classifier) {
685         return new ArtifactReference( ).groupId( groupId ).artifactId( artifactId ).version( version ).type( type ).classifier( classifier );
686     }
687
688
689     @Override
690     public void deleteVersion( VersionedReference ref ) throws ContentNotFoundException, ContentAccessException
691     {
692         final String path = toPath( ref );
693         final Path deleteTarget = getRepoDir().resolve(path);
694         if ( !Files.exists(deleteTarget) )
695         {
696             log.warn( "Version path for repository {} does not exist: {}", getId(), deleteTarget );
697             throw new ContentNotFoundException( "Version not found for repository "+getId()+": "+path );
698         }
699         if ( Files.isDirectory(deleteTarget) )
700         {
701             try
702             {
703                 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
704             }
705             catch ( IOException e )
706             {
707                 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
708                 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
709             }
710         } else {
711             log.warn( "Version path for repository {} is not a directory {}", getId(), deleteTarget );
712             throw new ContentNotFoundException( "Version path for repository "+getId()+" is not directory: " + path );
713         }
714     }
715
716     @Override
717     public void deleteProject( ProjectReference ref )
718         throws ContentNotFoundException, ContentAccessException
719     {
720         final String path = toPath( ref );
721         final Path deleteTarget = getRepoDir( ).resolve( path );
722         if ( !Files.exists(deleteTarget) )
723         {
724             log.warn( "Project path for repository {} does not exist: {}", getId(), deleteTarget );
725             throw new ContentNotFoundException( "Project not found for repository "+getId()+": "+path );
726         }
727         if ( Files.isDirectory(deleteTarget) )
728         {
729             try
730             {
731                 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
732             }
733             catch ( IOException e )
734             {
735                 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
736                 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
737             }
738         }
739         else
740         {
741             log.warn( "Project path for repository {} is not a directory {}", getId(), deleteTarget );
742             throw new ContentNotFoundException( "Project path for repository "+getId()+" is not directory: " + path );
743         }
744
745     }
746
747     @Override
748     public void deleteProject( String namespace, String projectId ) throws ContentNotFoundException, ContentAccessException
749     {
750         this.deleteProject( new ProjectReference().groupId( namespace ).artifactId( projectId ) );
751     }
752
753     @Override
754     public void deleteArtifact( ArtifactReference ref ) throws ContentNotFoundException, ContentAccessException
755     {
756         final String path = toPath( ref );
757         final Path repoDir = getRepoDir( );
758         Path deleteTarget = repoDir.resolve( path );
759         if ( Files.exists(deleteTarget) )
760         {
761             try
762             {
763                 if (Files.isDirectory( deleteTarget ))
764                 {
765                     org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
766                 } else {
767                     Files.delete( deleteTarget );
768                 }
769             }
770             catch ( IOException e )
771             {
772                 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
773                 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
774             }
775         } else {
776             log.warn( "Artifact path for repository {} does not exist: {}", getId(), deleteTarget );
777             throw new ContentNotFoundException( "Artifact not found for repository "+getId()+": "+path );
778         }
779
780     }
781
782     @Override
783     public void deleteGroupId( String groupId )
784         throws ContentNotFoundException, ContentAccessException
785     {
786         final String path = toPath( groupId );
787         final Path deleteTarget = getRepoDir( ).resolve( path );
788         if (!Files.exists(deleteTarget)) {
789             log.warn( "Namespace path for repository {} does not exist: {}", getId(), deleteTarget );
790             throw new ContentNotFoundException( "Namespace not found for repository "+getId()+": "+path );
791         }
792         if ( Files.isDirectory(deleteTarget) )
793         {
794             try
795             {
796                 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
797             }
798             catch ( IOException e )
799             {
800                 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
801                 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
802             }
803         } else {
804             log.warn( "Namespace path for repository {} is not a directory {}", getId(), deleteTarget );
805             throw new ContentNotFoundException( "Namespace path for repository "+getId()+" is not directory: " + path );
806
807         }
808     }
809
810     @Override
811     public String getId()
812     {
813         return repository.getId();
814     }
815
816     @Override
817     public List<ArtifactReference> getRelatedArtifacts( VersionedReference reference )
818         throws ContentNotFoundException, LayoutException, ContentAccessException
819     {
820         StorageAsset artifactDir = toFile( reference );
821         if ( !artifactDir.exists())
822         {
823             throw new ContentNotFoundException(
824                 "Unable to get related artifacts using a non-existant directory: " + artifactDir.getPath() );
825         }
826
827         if ( !artifactDir.isContainer() )
828         {
829             throw new ContentNotFoundException(
830                 "Unable to get related artifacts using a non-directory: " + artifactDir.getPath() );
831         }
832
833         // First gather up the versions found as artifacts in the managed repository.
834
835         try (Stream<? extends StorageAsset> stream = artifactDir.list().stream() ) {
836             return stream.filter(asset -> !asset.isContainer()).map(path -> {
837                 try {
838                     ArtifactReference artifact = toArtifactReference(path.getPath());
839                     if( artifact.getGroupId().equals( reference.getGroupId() ) && artifact.getArtifactId().equals(
840                             reference.getArtifactId() ) && artifact.getVersion().equals( reference.getVersion() )) {
841                         return artifact;
842                     } else {
843                         return null;
844                     }
845                 } catch (LayoutException e) {
846                     log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
847                     return null;
848                 }
849             }).filter(Objects::nonNull).collect(Collectors.toList());
850         } catch (RuntimeException e) {
851             Throwable cause = e.getCause( );
852             if (cause!=null) {
853                 if (cause instanceof LayoutException) {
854                     throw (LayoutException)cause;
855                 } else
856                 {
857                     throw new ContentAccessException( cause.getMessage( ), cause );
858                 }
859             } else {
860                 throw new ContentAccessException( e.getMessage( ), e );
861             }
862         }
863     }
864
865     /*
866      * Create the filter for various combinations of classifier and type
867      */
868     private Predicate<ArtifactReference> getChecker(ArtifactReference referenceObject, String extension) {
869         // TODO: Check, if extension is the correct parameter here
870         // We compare type with extension which works for artifacts like .jar.md5 but may
871         // be not the best way.
872
873         if (referenceObject.getClassifier()!=null && referenceObject.getType()!=null) {
874             return ((ArtifactReference a) ->
875                 referenceObject.getGroupId().equals( a.getGroupId() )
876                 && referenceObject.getArtifactId().equals( a.getArtifactId() )
877                 && referenceObject.getVersion( ).equals( a.getVersion( ) )
878                 && ( (a.getType()==null)
879                     || referenceObject.getType().equals( a.getType() )
880                     || a.getType().startsWith(extension) )
881                 && referenceObject.getClassifier().equals( a.getClassifier() )
882             );
883         } else if (referenceObject.getClassifier()!=null && referenceObject.getType()==null){
884             return ((ArtifactReference a) ->
885                 referenceObject.getGroupId().equals( a.getGroupId() )
886                     && referenceObject.getArtifactId().equals( a.getArtifactId() )
887                     && referenceObject.getVersion( ).equals( a.getVersion( ) )
888                     && referenceObject.getClassifier().equals( a.getClassifier() )
889             );
890         } else if (referenceObject.getClassifier()==null && referenceObject.getType()!=null){
891             return ((ArtifactReference a) ->
892                 referenceObject.getGroupId().equals( a.getGroupId() )
893                     && referenceObject.getArtifactId().equals( a.getArtifactId() )
894                     && referenceObject.getVersion( ).equals( a.getVersion( ) )
895                     && ( (a.getType()==null)
896                     || referenceObject.getType().equals( a.getType() )
897                     || a.getType().startsWith(extension) )
898             );
899         } else {
900             return ((ArtifactReference a) ->
901                 referenceObject.getGroupId().equals( a.getGroupId() )
902                     && referenceObject.getArtifactId().equals( a.getArtifactId() )
903                     && referenceObject.getVersion( ).equals( a.getVersion( ) )
904             );
905         }
906
907
908     }
909
910     @Override
911     public List<ArtifactReference> getRelatedArtifacts( ArtifactReference reference )
912         throws ContentNotFoundException, LayoutException, ContentAccessException
913     {
914         if ( StringUtils.isEmpty( reference.getType() ) && StringUtils.isEmpty( reference.getClassifier() ) ) {
915             return getRelatedArtifacts( toVersion( reference ) );
916         }
917
918         StorageAsset artifactFile = toFile( reference );
919         StorageAsset repoDir = artifactFile.getParent();
920         String ext;
921         if (!artifactFile.isContainer()) {
922             ext = StringUtils.substringAfterLast( artifactFile.getName(), ".");
923         } else {
924             ext = "";
925         }
926
927         if ( !repoDir.exists())
928         {
929             throw new ContentNotFoundException(
930                 "Unable to get related artifacts using a non-existant directory: " + repoDir.getPath() );
931         }
932
933         if ( !repoDir.isContainer() )
934         {
935             throw new ContentNotFoundException(
936                 "Unable to get related artifacts using a non-directory: " + repoDir.getPath() );
937         }
938
939         // First gather up the versions found as artifacts in the managed repository.
940
941         try (Stream<? extends StorageAsset> stream = repoDir.list().stream() ) {
942             return stream.filter(
943                 asset -> !asset.isContainer())
944                 .map(path -> {
945                 try {
946                     return toArtifactReference(path.getPath());
947                 } catch (LayoutException e) {
948                     log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
949                     return null;
950                 }
951             }).filter(Objects::nonNull).filter(getChecker( reference, ext )).collect(Collectors.toList());
952         } catch (RuntimeException e) {
953             Throwable cause = e.getCause( );
954             if (cause!=null) {
955                 if (cause instanceof LayoutException) {
956                     throw (LayoutException)cause;
957                 } else
958                 {
959                     throw new ContentAccessException( cause.getMessage( ), cause );
960                 }
961             } else {
962                 throw new ContentAccessException( e.getMessage( ), e );
963             }
964         }
965     }
966
967     @Override
968     public List<StorageAsset> getRelatedAssets( ArtifactReference reference ) throws ContentNotFoundException, LayoutException, ContentAccessException
969     {
970         return null;
971     }
972
973     @Override
974     public String getRepoRoot()
975     {
976         return convertUriToPath( repository.getLocation() );
977     }
978
979     private String convertUriToPath( URI uri ) {
980         if (uri.getScheme()==null) {
981             return Paths.get(uri.getPath()).toString();
982         } else if ("file".equals(uri.getScheme())) {
983             return Paths.get(uri).toString();
984         } else {
985             return uri.toString();
986         }
987     }
988
989     @Override
990     public ManagedRepository getRepository()
991     {
992         return repository;
993     }
994
995     /**
996      * Gather the Available Versions (on disk) for a specific Project Reference, based on filesystem
997      * information.
998      *
999      * @return the Set of available versions, based on the project reference.
1000      * @throws LayoutException
1001      */
1002     @Override
1003     public Set<String> getVersions( ProjectReference reference )
1004         throws ContentNotFoundException, LayoutException, ContentAccessException
1005     {
1006         final String path = toPath( reference );
1007         final Path projDir = getRepoDir().resolve(toPath(reference));
1008         if ( !Files.exists(projDir) )
1009         {
1010             throw new ContentNotFoundException(
1011                 "Unable to get Versions on a non-existant directory for repository "+getId()+": " + path );
1012         }
1013
1014         if ( !Files.isDirectory(projDir) )
1015         {
1016             throw new ContentNotFoundException(
1017                 "Unable to get Versions on a non-directory for repository "+getId()+": " + path );
1018         }
1019
1020         final String groupId = reference.getGroupId();
1021         final String artifactId = reference.getArtifactId();
1022         try(Stream<Path> stream = Files.list(projDir)) {
1023             return stream.filter(Files::isDirectory).map(
1024                     p -> toVersion(groupId, artifactId, p.getFileName().toString())
1025             ).filter(this::hasArtifact).map(ref -> ref.getVersion())
1026                     .collect(Collectors.toSet());
1027         } catch (IOException e) {
1028             log.error("Could not read directory {}: {}", projDir, e.getMessage(), e);
1029             throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, e );
1030         } catch (RuntimeException e) {
1031             Throwable cause = e.getCause( );
1032             if (cause!=null)
1033             {
1034                 if ( cause instanceof LayoutException )
1035                 {
1036                     throw (LayoutException) cause;
1037                 } else {
1038                     log.error("Could not read directory {}: {}", projDir, cause.getMessage(), cause);
1039                     throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, cause );
1040                 }
1041             } else {
1042                 log.error("Could not read directory {}: {}", projDir, e.getMessage(), e);
1043                 throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, cause );
1044             }
1045         }
1046     }
1047
1048     @Override
1049     public Set<String> getVersions( VersionedReference reference )
1050         throws ContentNotFoundException, ContentAccessException, LayoutException
1051     {
1052         try(Stream<ArtifactReference> stream = getArtifactStream( reference ))
1053         {
1054             return stream.filter( Objects::nonNull )
1055                 .map( ar -> ar.getVersion( ) )
1056                 .collect( Collectors.toSet( ) );
1057         } catch (IOException e) {
1058             final String path = toPath( reference );
1059             log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
1060             throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, e );
1061         }
1062     }
1063
1064     @Override
1065     public boolean hasContent( ArtifactReference reference ) throws ContentAccessException
1066     {
1067         StorageAsset artifactFile = toFile( reference );
1068         return artifactFile.exists() && !artifactFile.isContainer();
1069     }
1070
1071     @Override
1072     public boolean hasContent( ProjectReference reference ) throws ContentAccessException
1073     {
1074         try
1075         {
1076             Set<String> versions = getVersions( reference );
1077             return !versions.isEmpty();
1078         }
1079         catch ( ContentNotFoundException | LayoutException e )
1080         {
1081             return false;
1082         }
1083     }
1084
1085     @Override
1086     public boolean hasContent( VersionedReference reference ) throws ContentAccessException
1087     {
1088         try
1089         {
1090             return ( getFirstArtifact( reference ) != null );
1091         }
1092         catch ( LayoutException | ContentNotFoundException e )
1093         {
1094             return false;
1095         }
1096         catch ( IOException e )
1097         {
1098             String path = toPath( reference );
1099             log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
1100             throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
1101         }
1102     }
1103
1104     @Override
1105     public void setRepository( final ManagedRepository repo )
1106     {
1107         this.repository = repo;
1108         if (repo!=null) {
1109             if (repository instanceof EditableManagedRepository) {
1110                 ((EditableManagedRepository) repository).setContent(this);
1111             }
1112         }
1113     }
1114
1115     private Path getRepoDir() {
1116         return repository.getAsset( "" ).getFilePath( );
1117     }
1118
1119     private RepositoryStorage getStorage() {
1120         return repository.getAsset( "" ).getStorage( );
1121     }
1122
1123     /**
1124      * Convert a path to an artifact reference.
1125      *
1126      * @param path the path to convert. (relative or full location path)
1127      * @throws LayoutException if the path cannot be converted to an artifact reference.
1128      */
1129     @Override
1130     public ArtifactReference toArtifactReference( String path )
1131         throws LayoutException
1132     {
1133         String repoPath = convertUriToPath( repository.getLocation() );
1134         if ( ( path != null ) && path.startsWith( repoPath ) && repoPath.length() > 0 )
1135         {
1136             return super.toArtifactReference( path.substring( repoPath.length() + 1 ) );
1137         } else {
1138             repoPath = path;
1139             if (repoPath!=null) {
1140                 while (repoPath.startsWith("/")) {
1141                     repoPath = repoPath.substring(1);
1142                 }
1143             }
1144             return super.toArtifactReference( repoPath );
1145         }
1146     }
1147
1148
1149     // The variant with runtime exception for stream usage
1150     private ArtifactReference toArtifactRef(String path) {
1151         try {
1152             return toArtifactReference(path);
1153         } catch (LayoutException e) {
1154             throw new RuntimeException(e);
1155         }
1156     }
1157
1158
1159
1160     @Override
1161     public StorageAsset toFile( ArtifactReference reference )
1162     {
1163         return repository.getAsset(toPath(reference));
1164     }
1165
1166     @Override
1167     public StorageAsset toFile( ArchivaArtifact reference )
1168     {
1169         return repository.getAsset( toPath( reference ) );
1170     }
1171
1172     @Override
1173     public StorageAsset toFile( VersionedReference reference )
1174     {
1175         return repository.getAsset( toPath( reference ) );
1176     }
1177
1178     /**
1179      * Get the first Artifact found in the provided VersionedReference location.
1180      *
1181      * @param reference the reference to the versioned reference to search within
1182      * @return the ArtifactReference to the first artifact located within the versioned reference. or null if
1183      *         no artifact was found within the versioned reference.
1184      * @throws java.io.IOException     if the versioned reference is invalid (example: doesn't exist, or isn't a directory)
1185      * @throws LayoutException
1186      */
1187     private ArtifactReference getFirstArtifact( VersionedReference reference )
1188         throws ContentNotFoundException, LayoutException, IOException
1189     {
1190         try(Stream<ArtifactReference> stream = getArtifactStream( reference ))
1191         {
1192             return stream.findFirst( ).orElse( null );
1193         } catch (RuntimeException e) {
1194             throw new ContentNotFoundException( e.getMessage( ), e.getCause( ) );
1195         }
1196     }
1197
1198     private Stream<ArtifactReference> getArtifactStream(VersionedReference reference) throws ContentNotFoundException, LayoutException, IOException {
1199         final Path repoBase = getRepoDir( );
1200         String path = toMetadataPath( reference );
1201         Path versionDir = repoBase.resolve( path ).getParent();
1202         if ( !Files.exists(versionDir) )
1203         {
1204             throw new ContentNotFoundException( "Unable to gather the list of artifacts on a non-existant directory: "
1205                 + versionDir.toAbsolutePath() );
1206         }
1207
1208         if ( !Files.isDirectory(versionDir) )
1209         {
1210             throw new ContentNotFoundException(
1211                 "Unable to gather the list of snapshot versions on a non-directory: " + versionDir.toAbsolutePath() );
1212         }
1213         return Files.list(versionDir).filter(Files::isRegularFile)
1214                 .map(p -> repoBase.relativize(p).toString())
1215                 .filter(p -> !filetypes.matchesDefaultExclusions(p))
1216                 .filter(filetypes::matchesArtifactPattern)
1217                 .map(this::toArtifactRef);
1218     }
1219
1220     public List<ArtifactReference> getArtifacts(VersionedReference reference) throws ContentNotFoundException, LayoutException, ContentAccessException
1221     {
1222         try (Stream<ArtifactReference> stream = getArtifactStream( reference ))
1223         {
1224             return stream.collect( Collectors.toList( ) );
1225         } catch ( IOException e )
1226         {
1227             String path = toPath( reference );
1228             log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
1229             throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
1230
1231         }
1232     }
1233
1234     private boolean hasArtifact( VersionedReference reference )
1235
1236     {
1237         try(Stream<ArtifactReference> stream = getArtifactStream( reference ))
1238         {
1239             return stream.anyMatch( e -> true );
1240         } catch (ContentNotFoundException e) {
1241             return false;
1242         } catch ( LayoutException | IOException e) {
1243             // We throw the runtime exception for better stream handling
1244             throw new RuntimeException(e);
1245         }
1246     }
1247
1248     public void setFiletypes( FileTypes filetypes )
1249     {
1250         this.filetypes = filetypes;
1251     }
1252
1253     public void setMavenContentHelper( MavenContentHelper contentHelper) {
1254         this.mavenContentHelper = contentHelper;
1255     }
1256
1257
1258 }