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