]> source.dussan.org Git - archiva.git/blob
731a1063c41df8514ab9d078d025e8ee78824747
[archiva.git] /
1 package org.apache.archiva.repository.content.maven2;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import org.apache.archiva.common.filelock.FileLockManager;
23 import org.apache.archiva.common.utils.FileUtils;
24 import org.apache.archiva.common.utils.PathUtil;
25 import org.apache.archiva.common.utils.VersionUtil;
26 import org.apache.archiva.configuration.FileTypes;
27 import org.apache.archiva.metadata.repository.storage.maven2.ArtifactMappingProvider;
28 import org.apache.archiva.metadata.repository.storage.maven2.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.storage.StorageAsset;
49 import org.apache.commons.collections4.map.ReferenceMap;
50 import org.apache.commons.lang3.StringUtils;
51
52 import java.io.IOException;
53 import java.net.URI;
54 import java.nio.file.Files;
55 import java.nio.file.Path;
56 import java.nio.file.Paths;
57 import java.util.Collections;
58 import java.util.List;
59 import java.util.Objects;
60 import java.util.Set;
61 import java.util.function.Predicate;
62 import java.util.stream.Collectors;
63 import java.util.stream.Stream;
64
65 /**
66  * ManagedDefaultRepositoryContent
67  */
68 public class ManagedDefaultRepositoryContent
69     extends AbstractDefaultRepositoryContent
70     implements ManagedRepositoryContent
71 {
72
73     private FileTypes filetypes;
74
75     public void setFileTypes(FileTypes fileTypes) {
76         this.filetypes = fileTypes;
77     }
78
79     private ManagedRepository repository;
80
81     FileLockManager lockManager;
82
83     private ReferenceMap<String, Namespace> namespaceMap = new ReferenceMap<>( );
84     private ReferenceMap<StorageAsset, Project> projectMap = new ReferenceMap<>( );
85     private ReferenceMap<StorageAsset, Version> versionMap = new ReferenceMap<>( );
86     private ReferenceMap<StorageAsset, Artifact> artifactMap = new ReferenceMap<>( );
87
88     public ManagedDefaultRepositoryContent(ManagedRepository repository, FileTypes fileTypes, FileLockManager lockManager) {
89         super(Collections.singletonList( new DefaultArtifactMappingProvider() ));
90         setFileTypes( fileTypes );
91         this.lockManager = lockManager;
92         setRepository( repository );
93     }
94
95     public ManagedDefaultRepositoryContent( ManagedRepository repository, List<? extends ArtifactMappingProvider> artifactMappingProviders, FileTypes fileTypes, FileLockManager lockManager )
96     {
97         super(artifactMappingProviders==null ? Collections.singletonList( new DefaultArtifactMappingProvider() ) : artifactMappingProviders);
98         setFileTypes( fileTypes );
99         this.lockManager = lockManager;
100         setRepository( repository );
101
102     }
103
104     /**
105      * Returns a version reference from the coordinates
106      * @param groupId the group id
107      * @param artifactId the artifact id
108      * @param version the version
109      * @return the versioned reference object
110      */
111     @Override
112     public VersionedReference toVersion( String groupId, String artifactId, String version ) {
113         return new VersionedReference().groupId( groupId ).artifactId( artifactId ).version( version );
114     }
115
116     @Override
117     public VersionedReference toGenericVersion( ArtifactReference artifactReference )
118     {
119         return toVersion( artifactReference.getGroupId( ), artifactReference.getArtifactId( ), VersionUtil.getBaseVersion( artifactReference.getVersion( ) ));
120     }
121
122     /**
123      * Return the version the artifact is part of
124      * @param artifactReference
125      * @return
126      */
127     public VersionedReference toVersion( ArtifactReference artifactReference) {
128         return toVersion( artifactReference.getGroupId( ), artifactReference.getArtifactId( ), artifactReference.getVersion( ) );
129     }
130
131     @Override
132     public ArtifactReference toArtifact( String groupId, String artifactId, String version, String type, String classifier) {
133         return new ArtifactReference( ).groupId( groupId ).artifactId( artifactId ).version( version ).type( type ).classifier( classifier );
134     }
135
136     @Override
137     public void deleteItem( ContentItem item ) throws ItemNotFoundException, ContentAccessException
138     {
139         final Path baseDirectory = getRepoDir( );
140         final Path itemPath = item.getAsset( ).getFilePath( );
141         if ( !Files.exists( itemPath ) )
142         {
143             throw new ItemNotFoundException( "The item " + item.toString() + "does not exist in the repository " + getId( ) );
144         }
145         if ( !itemPath.toAbsolutePath().startsWith( baseDirectory.toAbsolutePath() ) )
146         {
147             log.error( "The namespace {} to delete from repository {} is not a subdirectory of the repository base.", item, getId( ) );
148             log.error( "Namespace directory: {}", itemPath );
149             log.error( "Repository directory: {}", baseDirectory );
150             throw new ContentAccessException( "Inconsistent directories found. Could not delete namespace." );
151         }
152         try
153         {
154             if (Files.isDirectory( itemPath ))
155             {
156                 FileUtils.deleteDirectory( itemPath );
157             } else {
158                 Files.deleteIfExists( itemPath );
159             }
160         }
161         catch ( IOException e )
162         {
163             log.error( "Could not delete namespace directory {}: {}", itemPath, e.getMessage( ), e );
164             throw new ContentAccessException( "Error occured while deleting namespace " + item + ": " + e.getMessage( ), e );
165         }
166     }
167
168     private StorageAsset getAsset(String namespace) {
169         String namespacePath = formatAsDirectory( namespace.trim() );
170         if (StringUtils.isEmpty( namespacePath )) {
171             namespacePath = "";
172         }
173         return getAsset(namespacePath);
174     }
175
176     private StorageAsset getAsset(String namespace, String project) {
177         return getAsset( namespace ).resolve( project );
178     }
179
180     private StorageAsset getAsset(String namespace, String project, String version) {
181         return getAsset( namespace, project ).resolve( version );
182     }
183
184     private StorageAsset getAsset(String namespace, String project, String version, String fileName) {
185         return getAsset( namespace, project, version ).resolve( fileName );
186     }
187
188     @Override
189     public Namespace getNamespace( final ItemSelector namespaceSelector ) throws ContentAccessException, IllegalArgumentException
190     {
191         return namespaceMap.computeIfAbsent( namespaceSelector.getNamespace(),
192             namespace -> {
193                 StorageAsset nsPath = getAsset( namespace );
194                 return ArchivaNamespace.withRepository( this ).withAsset( nsPath ).
195                     withNamespace( namespace ).build( );
196             });
197     }
198
199
200     @Override
201     public Project getProject( final ItemSelector projectSelector ) throws ContentAccessException, IllegalArgumentException
202     {
203         if (StringUtils.isEmpty( projectSelector.getProjectId() )) {
204             throw new IllegalArgumentException( "Project id must be set" );
205         }
206         final StorageAsset path = getAsset( projectSelector.getNamespace( ), projectSelector.getProjectId( ) );
207         return projectMap.computeIfAbsent( path, projectPath -> {
208             final Namespace ns = getNamespace( projectSelector );
209             return ArchivaProject.withAsset( projectPath ).withNamespace( ns ).withId( projectSelector.getProjectId( ) ).build( );
210         }
211         );
212     }
213
214
215     /*
216         TBD
217      */
218     @Override
219     public Version getVersion( ItemSelector versionCoordinates ) throws ContentAccessException, IllegalArgumentException
220     {
221         if (StringUtils.isEmpty( versionCoordinates.getVersion() )) {
222             throw new IllegalArgumentException( "Version must be set" );
223         }
224         return null;
225     }
226
227
228     /*
229         TBD
230      */
231     @Override
232     public Artifact getArtifact( ItemSelector selector ) throws ContentAccessException
233     {
234         return null;
235     }
236
237     /*
238         TBD
239      */
240     @Override
241     public List<Artifact> getAllArtifacts( ItemSelector selector ) throws ContentAccessException
242     {
243         return null;
244     }
245
246     /*
247         TBD
248      */
249     @Override
250     public Stream<Artifact> getAllArtifactStream( ItemSelector selector ) throws ContentAccessException
251     {
252         return null;
253     }
254
255     /*
256         TBD
257      */
258     @Override
259     public List<Project> getProjects( Namespace namespace )
260     {
261         return null;
262     }
263
264     /*
265         TBD
266      */
267     @Override
268     public List<Version> getVersions( Project project )
269     {
270         return null;
271     }
272
273     /*
274         TBD
275      */
276     @Override
277     public List<Artifact> getArtifacts( ContentItem item )
278     {
279         return null;
280     }
281
282     /*
283         TBD
284      */
285     @Override
286     public List<Artifact> getArtifactsStartingWith( Namespace namespace )
287     {
288         return null;
289     }
290
291     /*
292         TBD
293      */
294     @Override
295     public Stream<Artifact> getArtifactStream( ContentItem item )
296     {
297         return null;
298     }
299
300     /*
301         TBD
302      */
303     @Override
304     public Stream<Artifact> getArtifactStreamStartingWith( Namespace namespace )
305     {
306         return null;
307     }
308
309     /*
310         TBD
311      */
312     @Override
313     public boolean hasContent( ItemSelector selector )
314     {
315         return false;
316     }
317
318     /*
319         TBD
320      */
321     @Override
322     public void copyArtifact( Path sourceFile, ItemSelector destination ) throws IllegalArgumentException
323     {
324
325     }
326
327     @Override
328     public void deleteVersion( VersionedReference ref ) throws ContentNotFoundException, ContentAccessException
329     {
330         final String path = toPath( ref );
331         final Path deleteTarget = getRepoDir().resolve(path);
332         if ( !Files.exists(deleteTarget) )
333         {
334             log.warn( "Version path for repository {} does not exist: {}", getId(), deleteTarget );
335             throw new ContentNotFoundException( "Version not found for repository "+getId()+": "+path );
336         }
337         if ( Files.isDirectory(deleteTarget) )
338         {
339             try
340             {
341                 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
342             }
343             catch ( IOException e )
344             {
345                 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
346                 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
347             }
348         } else {
349             log.warn( "Version path for repository {} is not a directory {}", getId(), deleteTarget );
350             throw new ContentNotFoundException( "Version path for repository "+getId()+" is not directory: " + path );
351         }
352     }
353
354     @Override
355     public void deleteProject( ProjectReference ref )
356         throws ContentNotFoundException, ContentAccessException
357     {
358         final String path = toPath( ref );
359         final Path deleteTarget = getRepoDir( ).resolve( path );
360         if ( !Files.exists(deleteTarget) )
361         {
362             log.warn( "Project path for repository {} does not exist: {}", getId(), deleteTarget );
363             throw new ContentNotFoundException( "Project not found for repository "+getId()+": "+path );
364         }
365         if ( Files.isDirectory(deleteTarget) )
366         {
367             try
368             {
369                 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
370             }
371             catch ( IOException e )
372             {
373                 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
374                 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
375             }
376         }
377         else
378         {
379             log.warn( "Project path for repository {} is not a directory {}", getId(), deleteTarget );
380             throw new ContentNotFoundException( "Project path for repository "+getId()+" is not directory: " + path );
381         }
382
383     }
384
385     @Override
386     public void deleteProject( String namespace, String projectId ) throws ContentNotFoundException, ContentAccessException
387     {
388         this.deleteProject( new ProjectReference().groupId( namespace ).artifactId( projectId ) );
389     }
390
391     @Override
392     public void deleteArtifact( ArtifactReference ref ) throws ContentNotFoundException, ContentAccessException
393     {
394         final String path = toPath( ref );
395         final Path repoDir = getRepoDir( );
396         Path deleteTarget = repoDir.resolve( path );
397         if ( Files.exists(deleteTarget) )
398         {
399             try
400             {
401                 if (Files.isDirectory( deleteTarget ))
402                 {
403                     org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
404                 } else {
405                     Files.delete( deleteTarget );
406                 }
407             }
408             catch ( IOException e )
409             {
410                 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
411                 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
412             }
413         } else {
414             log.warn( "Artifact path for repository {} does not exist: {}", getId(), deleteTarget );
415             throw new ContentNotFoundException( "Artifact not found for repository "+getId()+": "+path );
416         }
417
418     }
419
420     @Override
421     public void deleteGroupId( String groupId )
422         throws ContentNotFoundException, ContentAccessException
423     {
424         final String path = toPath( groupId );
425         final Path deleteTarget = getRepoDir( ).resolve( path );
426         if (!Files.exists(deleteTarget)) {
427             log.warn( "Namespace path for repository {} does not exist: {}", getId(), deleteTarget );
428             throw new ContentNotFoundException( "Namespace not found for repository "+getId()+": "+path );
429         }
430         if ( Files.isDirectory(deleteTarget) )
431         {
432             try
433             {
434                 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
435             }
436             catch ( IOException e )
437             {
438                 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
439                 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
440             }
441         } else {
442             log.warn( "Namespace path for repository {} is not a directory {}", getId(), deleteTarget );
443             throw new ContentNotFoundException( "Namespace path for repository "+getId()+" is not directory: " + path );
444
445         }
446     }
447
448     @Override
449     public String getId()
450     {
451         return repository.getId();
452     }
453
454     @Override
455     public List<ArtifactReference> getRelatedArtifacts( VersionedReference reference )
456         throws ContentNotFoundException, LayoutException, ContentAccessException
457     {
458         StorageAsset artifactDir = toFile( reference );
459         if ( !artifactDir.exists())
460         {
461             throw new ContentNotFoundException(
462                 "Unable to get related artifacts using a non-existant directory: " + artifactDir.getPath() );
463         }
464
465         if ( !artifactDir.isContainer() )
466         {
467             throw new ContentNotFoundException(
468                 "Unable to get related artifacts using a non-directory: " + artifactDir.getPath() );
469         }
470
471         // First gather up the versions found as artifacts in the managed repository.
472
473         try (Stream<StorageAsset> stream = artifactDir.list().stream() ) {
474             return stream.filter(asset -> !asset.isContainer()).map(path -> {
475                 try {
476                     ArtifactReference artifact = toArtifactReference(path.getPath());
477                     if( artifact.getGroupId().equals( reference.getGroupId() ) && artifact.getArtifactId().equals(
478                             reference.getArtifactId() ) && artifact.getVersion().equals( reference.getVersion() )) {
479                         return artifact;
480                     } else {
481                         return null;
482                     }
483                 } catch (LayoutException e) {
484                     log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
485                     return null;
486                 }
487             }).filter(Objects::nonNull).collect(Collectors.toList());
488         } catch (RuntimeException e) {
489             Throwable cause = e.getCause( );
490             if (cause!=null) {
491                 if (cause instanceof LayoutException) {
492                     throw (LayoutException)cause;
493                 } else
494                 {
495                     throw new ContentAccessException( cause.getMessage( ), cause );
496                 }
497             } else {
498                 throw new ContentAccessException( e.getMessage( ), e );
499             }
500         }
501     }
502
503     /*
504      * Create the filter for various combinations of classifier and type
505      */
506     private Predicate<ArtifactReference> getChecker(ArtifactReference referenceObject, String extension) {
507         // TODO: Check, if extension is the correct parameter here
508         // We compare type with extension which works for artifacts like .jar.md5 but may
509         // be not the best way.
510
511         if (referenceObject.getClassifier()!=null && referenceObject.getType()!=null) {
512             return ((ArtifactReference a) ->
513                 referenceObject.getGroupId().equals( a.getGroupId() )
514                 && referenceObject.getArtifactId().equals( a.getArtifactId() )
515                 && referenceObject.getVersion( ).equals( a.getVersion( ) )
516                 && ( (a.getType()==null)
517                     || referenceObject.getType().equals( a.getType() )
518                     || a.getType().startsWith(extension) )
519                 && referenceObject.getClassifier().equals( a.getClassifier() )
520             );
521         } else if (referenceObject.getClassifier()!=null && referenceObject.getType()==null){
522             return ((ArtifactReference a) ->
523                 referenceObject.getGroupId().equals( a.getGroupId() )
524                     && referenceObject.getArtifactId().equals( a.getArtifactId() )
525                     && referenceObject.getVersion( ).equals( a.getVersion( ) )
526                     && referenceObject.getClassifier().equals( a.getClassifier() )
527             );
528         } else if (referenceObject.getClassifier()==null && referenceObject.getType()!=null){
529             return ((ArtifactReference a) ->
530                 referenceObject.getGroupId().equals( a.getGroupId() )
531                     && referenceObject.getArtifactId().equals( a.getArtifactId() )
532                     && referenceObject.getVersion( ).equals( a.getVersion( ) )
533                     && ( (a.getType()==null)
534                     || referenceObject.getType().equals( a.getType() )
535                     || a.getType().startsWith(extension) )
536             );
537         } else {
538             return ((ArtifactReference a) ->
539                 referenceObject.getGroupId().equals( a.getGroupId() )
540                     && referenceObject.getArtifactId().equals( a.getArtifactId() )
541                     && referenceObject.getVersion( ).equals( a.getVersion( ) )
542             );
543         }
544
545
546     }
547
548     @Override
549     public List<ArtifactReference> getRelatedArtifacts( ArtifactReference reference )
550         throws ContentNotFoundException, LayoutException, ContentAccessException
551     {
552         if ( StringUtils.isEmpty( reference.getType() ) && StringUtils.isEmpty( reference.getClassifier() ) ) {
553             return getRelatedArtifacts( toVersion( reference ) );
554         }
555
556         StorageAsset artifactFile = toFile( reference );
557         StorageAsset repoDir = artifactFile.getParent();
558         String ext;
559         if (!artifactFile.isContainer()) {
560             ext = StringUtils.substringAfterLast( artifactFile.getName(), ".");
561         } else {
562             ext = "";
563         }
564
565         if ( !repoDir.exists())
566         {
567             throw new ContentNotFoundException(
568                 "Unable to get related artifacts using a non-existant directory: " + repoDir.getPath() );
569         }
570
571         if ( !repoDir.isContainer() )
572         {
573             throw new ContentNotFoundException(
574                 "Unable to get related artifacts using a non-directory: " + repoDir.getPath() );
575         }
576
577         // First gather up the versions found as artifacts in the managed repository.
578
579         try (Stream<StorageAsset> stream = repoDir.list().stream() ) {
580             return stream.filter(
581                 asset -> !asset.isContainer())
582                 .map(path -> {
583                 try {
584                     return toArtifactReference(path.getPath());
585                 } catch (LayoutException e) {
586                     log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
587                     return null;
588                 }
589             }).filter(Objects::nonNull).filter(getChecker( reference, ext )).collect(Collectors.toList());
590         } catch (RuntimeException e) {
591             Throwable cause = e.getCause( );
592             if (cause!=null) {
593                 if (cause instanceof LayoutException) {
594                     throw (LayoutException)cause;
595                 } else
596                 {
597                     throw new ContentAccessException( cause.getMessage( ), cause );
598                 }
599             } else {
600                 throw new ContentAccessException( e.getMessage( ), e );
601             }
602         }
603     }
604
605     @Override
606     public List<StorageAsset> getRelatedAssets( ArtifactReference reference ) throws ContentNotFoundException, LayoutException, ContentAccessException
607     {
608         return null;
609     }
610
611     @Override
612     public String getRepoRoot()
613     {
614         return convertUriToPath( repository.getLocation() );
615     }
616
617     private String convertUriToPath( URI uri ) {
618         if (uri.getScheme()==null) {
619             return Paths.get(uri.getPath()).toString();
620         } else if ("file".equals(uri.getScheme())) {
621             return Paths.get(uri).toString();
622         } else {
623             return uri.toString();
624         }
625     }
626
627     @Override
628     public ManagedRepository getRepository()
629     {
630         return repository;
631     }
632
633     /**
634      * Gather the Available Versions (on disk) for a specific Project Reference, based on filesystem
635      * information.
636      *
637      * @return the Set of available versions, based on the project reference.
638      * @throws LayoutException
639      */
640     @Override
641     public Set<String> getVersions( ProjectReference reference )
642         throws ContentNotFoundException, LayoutException, ContentAccessException
643     {
644         final String path = toPath( reference );
645         final Path projDir = getRepoDir().resolve(toPath(reference));
646         if ( !Files.exists(projDir) )
647         {
648             throw new ContentNotFoundException(
649                 "Unable to get Versions on a non-existant directory for repository "+getId()+": " + path );
650         }
651
652         if ( !Files.isDirectory(projDir) )
653         {
654             throw new ContentNotFoundException(
655                 "Unable to get Versions on a non-directory for repository "+getId()+": " + path );
656         }
657
658         final String groupId = reference.getGroupId();
659         final String artifactId = reference.getArtifactId();
660         try(Stream<Path> stream = Files.list(projDir)) {
661             return stream.filter(Files::isDirectory).map(
662                     p -> toVersion(groupId, artifactId, p.getFileName().toString())
663             ).filter(this::hasArtifact).map(ref -> ref.getVersion())
664                     .collect(Collectors.toSet());
665         } catch (IOException e) {
666             log.error("Could not read directory {}: {}", projDir, e.getMessage(), e);
667             throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, e );
668         } catch (RuntimeException e) {
669             Throwable cause = e.getCause( );
670             if (cause!=null)
671             {
672                 if ( cause instanceof LayoutException )
673                 {
674                     throw (LayoutException) cause;
675                 } else {
676                     log.error("Could not read directory {}: {}", projDir, cause.getMessage(), cause);
677                     throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, cause );
678                 }
679             } else {
680                 log.error("Could not read directory {}: {}", projDir, e.getMessage(), e);
681                 throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, cause );
682             }
683         }
684     }
685
686     @Override
687     public Set<String> getVersions( VersionedReference reference )
688         throws ContentNotFoundException, ContentAccessException, LayoutException
689     {
690         try(Stream<ArtifactReference> stream = getArtifactStream( reference ))
691         {
692             return stream.filter( Objects::nonNull )
693                 .map( ar -> ar.getVersion( ) )
694                 .collect( Collectors.toSet( ) );
695         } catch (IOException e) {
696             final String path = toPath( reference );
697             log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
698             throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, e );
699         }
700     }
701
702     @Override
703     public boolean hasContent( ArtifactReference reference ) throws ContentAccessException
704     {
705         StorageAsset artifactFile = toFile( reference );
706         return artifactFile.exists() && !artifactFile.isContainer();
707     }
708
709     @Override
710     public boolean hasContent( ProjectReference reference ) throws ContentAccessException
711     {
712         try
713         {
714             Set<String> versions = getVersions( reference );
715             return !versions.isEmpty();
716         }
717         catch ( ContentNotFoundException | LayoutException e )
718         {
719             return false;
720         }
721     }
722
723     @Override
724     public boolean hasContent( VersionedReference reference ) throws ContentAccessException
725     {
726         try
727         {
728             return ( getFirstArtifact( reference ) != null );
729         }
730         catch ( LayoutException | ContentNotFoundException e )
731         {
732             return false;
733         }
734         catch ( IOException e )
735         {
736             String path = toPath( reference );
737             log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
738             throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
739         }
740     }
741
742     @Override
743     public void setRepository( final ManagedRepository repo )
744     {
745         this.repository = repo;
746         if (repo!=null) {
747             if (repository instanceof EditableManagedRepository) {
748                 ((EditableManagedRepository) repository).setContent(this);
749             }
750         }
751     }
752
753     private Path getRepoDir() {
754         return repository.getAsset( "" ).getFilePath( );
755     }
756
757     /**
758      * Convert a path to an artifact reference.
759      *
760      * @param path the path to convert. (relative or full location path)
761      * @throws LayoutException if the path cannot be converted to an artifact reference.
762      */
763     @Override
764     public ArtifactReference toArtifactReference( String path )
765         throws LayoutException
766     {
767         String repoPath = convertUriToPath( repository.getLocation() );
768         if ( ( path != null ) && path.startsWith( repoPath ) && repoPath.length() > 0 )
769         {
770             return super.toArtifactReference( path.substring( repoPath.length() + 1 ) );
771         } else {
772             repoPath = path;
773             if (repoPath!=null) {
774                 while (repoPath.startsWith("/")) {
775                     repoPath = repoPath.substring(1);
776                 }
777             }
778             return super.toArtifactReference( repoPath );
779         }
780     }
781
782
783     // The variant with runtime exception for stream usage
784     private ArtifactReference toArtifactRef(String path) {
785         try {
786             return toArtifactReference(path);
787         } catch (LayoutException e) {
788             throw new RuntimeException(e);
789         }
790     }
791
792
793
794     @Override
795     public StorageAsset toFile( ArtifactReference reference )
796     {
797         return repository.getAsset(toPath(reference));
798     }
799
800     @Override
801     public StorageAsset toFile( ArchivaArtifact reference )
802     {
803         return repository.getAsset( toPath( reference ) );
804     }
805
806     @Override
807     public StorageAsset toFile( VersionedReference reference )
808     {
809         return repository.getAsset( toPath( reference ) );
810     }
811
812     /**
813      * Get the first Artifact found in the provided VersionedReference location.
814      *
815      * @param reference the reference to the versioned reference to search within
816      * @return the ArtifactReference to the first artifact located within the versioned reference. or null if
817      *         no artifact was found within the versioned reference.
818      * @throws java.io.IOException     if the versioned reference is invalid (example: doesn't exist, or isn't a directory)
819      * @throws LayoutException
820      */
821     private ArtifactReference getFirstArtifact( VersionedReference reference )
822         throws ContentNotFoundException, LayoutException, IOException
823     {
824         try(Stream<ArtifactReference> stream = getArtifactStream( reference ))
825         {
826             return stream.findFirst( ).orElse( null );
827         } catch (RuntimeException e) {
828             throw new ContentNotFoundException( e.getMessage( ), e.getCause( ) );
829         }
830     }
831
832     private Stream<ArtifactReference> getArtifactStream(VersionedReference reference) throws ContentNotFoundException, LayoutException, IOException {
833         final Path repoBase = getRepoDir( );
834         String path = toMetadataPath( reference );
835         Path versionDir = repoBase.resolve( path ).getParent();
836         if ( !Files.exists(versionDir) )
837         {
838             throw new ContentNotFoundException( "Unable to gather the list of artifacts on a non-existant directory: "
839                 + versionDir.toAbsolutePath() );
840         }
841
842         if ( !Files.isDirectory(versionDir) )
843         {
844             throw new ContentNotFoundException(
845                 "Unable to gather the list of snapshot versions on a non-directory: " + versionDir.toAbsolutePath() );
846         }
847         return Files.list(versionDir).filter(Files::isRegularFile)
848                 .map(p -> repoBase.relativize(p).toString())
849                 .filter(p -> !filetypes.matchesDefaultExclusions(p))
850                 .filter(filetypes::matchesArtifactPattern)
851                 .map(this::toArtifactRef);
852     }
853
854     public List<ArtifactReference> getArtifacts(VersionedReference reference) throws ContentNotFoundException, LayoutException, ContentAccessException
855     {
856         try (Stream<ArtifactReference> stream = getArtifactStream( reference ))
857         {
858             return stream.collect( Collectors.toList( ) );
859         } catch ( IOException e )
860         {
861             String path = toPath( reference );
862             log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
863             throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
864
865         }
866     }
867
868     private boolean hasArtifact( VersionedReference reference )
869
870     {
871         try(Stream<ArtifactReference> stream = getArtifactStream( reference ))
872         {
873             return stream.anyMatch( e -> true );
874         } catch (ContentNotFoundException e) {
875             return false;
876         } catch ( LayoutException | IOException e) {
877             // We throw the runtime exception for better stream handling
878             throw new RuntimeException(e);
879         }
880     }
881
882     public void setFiletypes( FileTypes filetypes )
883     {
884         this.filetypes = filetypes;
885     }
886
887
888 }