1 package org.apache.archiva.repository.maven.content;
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
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
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.model.ArchivaArtifact;
28 import org.apache.archiva.model.ArtifactReference;
29 import org.apache.archiva.model.ProjectReference;
30 import org.apache.archiva.model.VersionedReference;
31 import org.apache.archiva.repository.ContentAccessException;
32 import org.apache.archiva.repository.ContentNotFoundException;
33 import org.apache.archiva.repository.EditableManagedRepository;
34 import org.apache.archiva.repository.ManagedRepositoryContent;
35 import org.apache.archiva.repository.ItemDeleteStatus;
36 import org.apache.archiva.repository.LayoutException;
37 import org.apache.archiva.repository.ManagedRepository;
38 import org.apache.archiva.repository.BaseRepositoryContentLayout;
39 import org.apache.archiva.repository.ManagedRepositoryContentLayout;
40 import org.apache.archiva.repository.content.Artifact;
41 import org.apache.archiva.repository.content.ArtifactType;
42 import org.apache.archiva.repository.content.BaseArtifactTypes;
43 import org.apache.archiva.repository.content.ContentItem;
44 import org.apache.archiva.repository.content.DataItem;
45 import org.apache.archiva.repository.content.ItemNotFoundException;
46 import org.apache.archiva.repository.content.ItemSelector;
47 import org.apache.archiva.repository.content.Namespace;
48 import org.apache.archiva.repository.content.Project;
49 import org.apache.archiva.repository.content.Version;
50 import org.apache.archiva.repository.content.base.ArchivaContentItem;
51 import org.apache.archiva.repository.content.base.ArchivaItemSelector;
52 import org.apache.archiva.repository.content.base.ArchivaNamespace;
53 import org.apache.archiva.repository.content.base.ArchivaProject;
54 import org.apache.archiva.repository.content.base.ArchivaVersion;
55 import org.apache.archiva.repository.content.base.builder.ArtifactOptBuilder;
56 import org.apache.archiva.repository.maven.metadata.storage.ArtifactMappingProvider;
57 import org.apache.archiva.repository.maven.metadata.storage.DefaultArtifactMappingProvider;
58 import org.apache.archiva.repository.storage.RepositoryStorage;
59 import org.apache.archiva.repository.storage.StorageAsset;
60 import org.apache.archiva.repository.storage.util.StorageUtil;
61 import org.apache.commons.collections4.map.ReferenceMap;
62 import org.apache.commons.lang3.StringUtils;
64 import javax.inject.Inject;
65 import javax.inject.Named;
66 import java.io.IOException;
68 import java.nio.file.Files;
69 import java.nio.file.Path;
70 import java.nio.file.Paths;
71 import java.util.Arrays;
72 import java.util.Collections;
73 import java.util.List;
74 import java.util.Objects;
75 import java.util.function.Consumer;
76 import java.util.function.Predicate;
77 import java.util.regex.Matcher;
78 import java.util.regex.Pattern;
79 import java.util.stream.Collectors;
80 import java.util.stream.Stream;
83 * ManagedDefaultRepositoryContent
85 public class ManagedDefaultRepositoryContent
86 extends AbstractDefaultRepositoryContent
87 implements BaseRepositoryContentLayout
90 // attribute flag that marks version objects that point to a snapshot artifact version
91 public static final String SNAPSHOT_ARTIFACT_VERSION = "maven.snav";
93 private FileTypes filetypes;
95 public void setFileTypes( FileTypes fileTypes )
97 this.filetypes = fileTypes;
100 private ManagedRepository repository;
102 private FileLockManager lockManager;
105 @Named( "repositoryPathTranslator#maven2" )
106 private RepositoryPathTranslator pathTranslator;
109 @Named( "metadataReader#maven" )
110 MavenMetadataReader metadataReader;
113 @Named( "MavenContentHelper" )
114 MavenContentHelper mavenContentHelper;
116 public static final String SNAPSHOT = "SNAPSHOT";
118 public static final Pattern UNIQUE_SNAPSHOT_PATTERN = Pattern.compile( "^(SNAPSHOT|[0-9]{8}\\.[0-9]{6}-[0-9]+)(.*)" );
119 public static final Pattern CLASSIFIER_PATTERN = Pattern.compile( "^-([^.]+)(\\..*)" );
120 public static final Pattern COMMON_EXTENSIONS = Pattern.compile( "^(jar|war|ear|dar|tar|zip|pom|xml)$" );
122 public static final Pattern TIMESTAMP_PATTERN = Pattern.compile( "^([0-9]{8})\\.([0-9]{6})$" );
124 public static final Pattern GENERIC_SNAPSHOT_PATTERN = Pattern.compile( "^(.*)-" + SNAPSHOT );
127 * We are caching content items in a weak reference map. To avoid always recreating the
128 * the hierarchical structure.
129 * TODO: Better use a object cache? E.g. our spring cache implementation?
131 private ReferenceMap<StorageAsset, ContentItem> itemMap = new ReferenceMap<>( );
132 private ReferenceMap<StorageAsset, DataItem> dataItemMap = new ReferenceMap<>( );
134 public ManagedDefaultRepositoryContent( )
136 super( Collections.singletonList( new DefaultArtifactMappingProvider( ) ) );
139 public ManagedDefaultRepositoryContent( ManagedRepository repository, FileTypes fileTypes, FileLockManager lockManager )
141 super( Collections.singletonList( new DefaultArtifactMappingProvider( ) ) );
142 setFileTypes( fileTypes );
143 this.lockManager = lockManager;
144 setRepository( repository );
147 public ManagedDefaultRepositoryContent( ManagedRepository repository, List<? extends ArtifactMappingProvider> artifactMappingProviders, FileTypes fileTypes, FileLockManager lockManager )
149 super( artifactMappingProviders == null ? Collections.singletonList( new DefaultArtifactMappingProvider( ) ) : artifactMappingProviders );
150 setFileTypes( fileTypes );
151 this.lockManager = lockManager;
152 setRepository( repository );
156 private StorageAsset getAssetByPath( String assetPath )
158 return getStorage( ).getAsset( assetPath );
161 private StorageAsset getAsset( String namespace )
163 String namespacePath = formatAsDirectory( namespace.trim( ) );
164 if ( StringUtils.isEmpty( namespacePath ) )
168 return getAssetByPath( namespacePath );
171 private StorageAsset getAsset( String namespace, String project )
173 return getAsset( namespace ).resolve( project );
176 private StorageAsset getAsset( String namespace, String project, String version )
178 return getAsset( namespace, project ).resolve( version );
181 private StorageAsset getAsset( String namespace, String project, String version, String fileName )
183 return getAsset( namespace, project, version ).resolve( fileName );
187 /// ************* Start of new generation interface ******************
191 public <T extends ContentItem> T adaptItem( Class<T> clazz, ContentItem item ) throws LayoutException
193 if (clazz.isAssignableFrom( Version.class ))
195 if ( !item.hasCharacteristic( Version.class ) )
197 item.setCharacteristic( Version.class, createVersionFromPath( item.getAsset() ) );
199 return (T) item.adapt( Version.class );
200 } else if ( clazz.isAssignableFrom( Project.class )) {
201 if ( !item.hasCharacteristic( Project.class ) )
203 item.setCharacteristic( Project.class, createProjectFromPath( item.getAsset() ) );
205 return (T) item.adapt( Project.class );
206 } else if ( clazz.isAssignableFrom( Namespace.class )) {
207 if ( !item.hasCharacteristic( Namespace.class ) )
209 item.setCharacteristic( Namespace.class, createNamespaceFromPath( item.getAsset() ) );
211 return (T) item.adapt( Namespace.class );
212 } else if ( clazz.isAssignableFrom( Artifact.class )) {
213 if (!item.hasCharacteristic( Artifact.class )) {
214 item.setCharacteristic( Artifact.class, createArtifactFromPath( item.getAsset( ) ) );
216 return (T) item.adapt( Artifact.class );
218 throw new LayoutException( "Could not convert item to class " + clazz);
223 public void deleteAllItems( ItemSelector selector, Consumer<ItemDeleteStatus> consumer ) throws ContentAccessException, IllegalArgumentException
225 try ( Stream<? extends ContentItem> stream = newItemStream( selector, false ) )
227 stream.forEach( item -> {
231 consumer.accept( new ItemDeleteStatus( item ) );
233 catch ( ItemNotFoundException e )
235 consumer.accept( new ItemDeleteStatus( item, ItemDeleteStatus.ITEM_NOT_FOUND, e ) );
237 catch ( Exception e )
239 consumer.accept( new ItemDeleteStatus( item, ItemDeleteStatus.DELETION_FAILED, e ) );
241 catch ( Throwable e )
243 consumer.accept( new ItemDeleteStatus( item, ItemDeleteStatus.UNKNOWN, e ) );
250 * Removes the item from the filesystem. For namespaces, projects and versions it deletes
252 * For namespaces you have to be careful, because maven repositories may have sub namespaces
253 * parallel to projects. Which means deleting a namespaces also deletes the sub namespaces and
254 * not only the projects of the given namespace. Better run the delete for each project of
257 * Artifacts are deleted as provided. No related artifacts will be deleted.
259 * @param item the item that should be removed
260 * @throws ItemNotFoundException if the item does not exist
261 * @throws ContentAccessException if some error occurred while accessing the filesystem
264 public void deleteItem( ContentItem item ) throws ItemNotFoundException, ContentAccessException
266 final Path baseDirectory = getRepoDir( );
267 final Path itemPath = item.getAsset( ).getFilePath( );
268 if ( !Files.exists( itemPath ) )
270 throw new ItemNotFoundException( "The item " + item.toString( ) + "does not exist in the repository " + getId( ) );
272 if ( !itemPath.toAbsolutePath( ).startsWith( baseDirectory.toAbsolutePath( ) ) )
274 log.error( "The namespace {} to delete from repository {} is not a subdirectory of the repository base.", item, getId( ) );
275 log.error( "Namespace directory: {}", itemPath );
276 log.error( "Repository directory: {}", baseDirectory );
277 throw new ContentAccessException( "Inconsistent directories found. Could not delete namespace." );
281 if ( Files.isDirectory( itemPath ) )
283 FileUtils.deleteDirectory( itemPath );
287 Files.deleteIfExists( itemPath );
290 catch ( IOException e )
292 log.error( "Could not delete item from path {}: {}", itemPath, e.getMessage( ), e );
293 throw new ContentAccessException( "Error occured while deleting item " + item + ": " + e.getMessage( ), e );
298 public ContentItem getItem( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
300 if ( selector.hasVersion( ) && selector.hasArtifactId( ) )
302 return getArtifact( selector );
304 else if ( selector.hasProjectId( ) && selector.hasVersion( ) )
306 return getVersion( selector );
308 else if ( selector.hasProjectId( ) )
310 return getProject( selector );
314 return getNamespace( selector );
319 public Namespace getNamespace( final ItemSelector namespaceSelector ) throws ContentAccessException, IllegalArgumentException
321 StorageAsset nsPath = getAsset( namespaceSelector.getNamespace() );
322 return getNamespaceFromPath( nsPath );
327 public Project getProject( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
329 if ( !selector.hasProjectId( ) )
331 throw new IllegalArgumentException( "Project id must be set" );
333 final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ) );
334 return getProjectFromPath( path );
339 public Version getVersion( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
341 if ( !selector.hasProjectId( ) )
343 throw new IllegalArgumentException( "Project id must be set" );
345 if ( !selector.hasVersion( ) )
347 throw new IllegalArgumentException( "Version must be set" );
349 final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ), selector.getVersion( ) );
350 return getVersionFromPath( path );
354 public Artifact createArtifact( final StorageAsset artifactPath, final ItemSelector selector,
355 final String classifier, final String extension )
357 Version version = getVersion( selector );
358 ArtifactOptBuilder builder = org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
359 .withVersion( version )
360 .withId( selector.getArtifactId( ) )
361 .withArtifactVersion( mavenContentHelper.getArtifactVersion( artifactPath, selector ) )
362 .withClassifier( classifier );
363 if ( selector.hasType( ) )
365 builder.withType( selector.getType( ) );
367 return builder.build( );
370 public Namespace getNamespaceFromArtifactPath( final StorageAsset artifactPath )
372 final StorageAsset namespacePath = artifactPath.getParent( ).getParent( ).getParent( );
373 return getNamespaceFromPath( namespacePath );
376 public Namespace getNamespaceFromPath( final StorageAsset nsPath )
378 ContentItem item = itemMap.computeIfAbsent( nsPath,
379 path -> createNamespaceFromPath( nsPath ) );
380 if (!item.hasCharacteristic( Namespace.class )) {
381 item.setCharacteristic( Namespace.class, createNamespaceFromPath( nsPath ) );
383 return item.adapt( Namespace.class );
386 public Namespace createNamespaceFromPath( final StorageAsset namespacePath) {
387 final String namespace = MavenContentHelper.getNamespaceFromNamespacePath( namespacePath );
388 return ArchivaNamespace.withRepository( this )
389 .withAsset( namespacePath )
390 .withNamespace( namespace )
394 private Project getProjectFromPath( final StorageAsset path )
396 ContentItem item = itemMap.computeIfAbsent( path, projectPath ->
397 createProjectFromPath( projectPath )
399 if (!item.hasCharacteristic( Project.class )) {
400 item.setCharacteristic( Project.class, createProjectFromPath( path ) );
402 return item.adapt( Project.class );
405 private Project createProjectFromPath( final StorageAsset projectPath ) {
406 Namespace namespace = getNamespaceFromPath( projectPath.getParent( ) );
407 return ArchivaProject.withRepository( this ).withAsset( projectPath )
408 .withNamespace( namespace )
409 .withId( projectPath.getName( ) ).build( );
412 private Project getProjectFromArtifactPath( final StorageAsset artifactPath )
414 final StorageAsset projectPath = artifactPath.getParent( ).getParent( );
415 return getProjectFromPath( projectPath );
418 private Version getVersionFromArtifactPath( final StorageAsset artifactPath )
420 final StorageAsset versionPath = artifactPath.getParent( );
421 return getVersionFromPath( versionPath );
424 private Version getVersionFromPath( StorageAsset path )
426 ContentItem item = itemMap.computeIfAbsent( path, versionPath ->
427 createVersionFromPath( versionPath )
429 if (!item.hasCharacteristic( Version.class )) {
430 item.setCharacteristic( Version.class, createVersionFromPath( path ) );
432 return item.adapt( Version.class );
435 private Version createVersionFromPath(StorageAsset path) {
436 Project proj = getProjectFromPath( path.getParent( ) );
437 return ArchivaVersion.withRepository( this ).withAsset( path )
438 .withProject( proj ).withVersion(path.getName()).build();
441 private Artifact getArtifactFromPath( final StorageAsset artifactPath )
443 DataItem item = dataItemMap.computeIfAbsent( artifactPath, myArtifactPath ->
444 createArtifactFromPath( myArtifactPath )
446 if (!item.hasCharacteristic( Artifact.class )) {
447 item.setCharacteristic( Artifact.class, createArtifactFromPath( artifactPath ) );
449 return item.adapt( Artifact.class );
452 private Artifact createArtifactFromPath( final StorageAsset artifactPath ) {
453 final Version version = getVersionFromArtifactPath( artifactPath );
454 final ArtifactInfo info = getArtifactInfoFromPath( version.getVersion( ), artifactPath );
455 return org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
456 .withVersion( version )
458 .withClassifier( info.classifier )
459 .withRemainder( info.remainder )
460 .withType( info.type )
461 .withArtifactVersion( info.version )
462 .withContentType( info.contentType )
463 .withArtifactType( info.artifactType )
467 private String getContentType(StorageAsset artifactPath) {
470 return Files.probeContentType( artifactPath.getFilePath( ) );
473 catch ( IOException e )
479 private DataItem getDataItemFromPath( final StorageAsset artifactPath )
481 final String extension = StringUtils.substringAfterLast( artifactPath.getName( ), "." );
482 final String contentType = getContentType( artifactPath );
483 return dataItemMap.computeIfAbsent( artifactPath, myArtifactPath ->
484 org.apache.archiva.repository.content.base.ArchivaDataItem.withAsset( artifactPath )
485 .withId( artifactPath.getName( ) )
486 .withContentType( contentType )
492 private ContentItem getItemFromPath( final StorageAsset itemPath )
494 if ( itemPath.isLeaf( ) )
496 if (dataItemMap.containsKey( itemPath )) {
497 return dataItemMap.get( itemPath );
499 return getDataItemFromPath( itemPath );
503 if (itemMap.containsKey( itemPath )) {
504 return itemMap.get( itemPath );
506 return ArchivaContentItem.withRepository( this ).withAsset( itemPath ).build();
512 public ManagedRepositoryContent getGenericContent( )
517 // Simple object to hold artifact information
518 private class ArtifactInfo
521 private String version;
522 private String extension;
523 private String remainder;
525 private String classifier;
526 private String contentType;
527 private StorageAsset asset;
528 private ArtifactType artifactType = BaseArtifactTypes.MAIN;
531 private ArtifactInfo getArtifactInfoFromPath( String genericVersion, StorageAsset path )
533 final ArtifactInfo info = new ArtifactInfo( );
535 info.id = path.getParent( ).getParent( ).getName( );
536 final String fileName = path.getName( );
537 if ( genericVersion.endsWith( "-" + SNAPSHOT ) )
539 String baseVersion = StringUtils.substringBeforeLast( genericVersion, "-" + SNAPSHOT );
540 String prefix = info.id + "-" + baseVersion + "-";
541 if ( fileName.startsWith( prefix ) )
543 String versionPostfix = StringUtils.removeStart( fileName, prefix );
544 Matcher matcher = UNIQUE_SNAPSHOT_PATTERN.matcher( versionPostfix );
545 if ( matcher.matches( ) )
547 info.version = baseVersion + "-" + matcher.group( 1 );
548 String newPrefix = info.id + "-" + info.version;
549 if ( fileName.startsWith( newPrefix ) )
551 String classPostfix = StringUtils.removeStart( fileName, newPrefix );
552 Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
553 if ( cMatch.matches( ) )
555 info.classifier = cMatch.group( 1 );
556 info.remainder = cMatch.group( 2 );
560 info.classifier = "";
561 info.remainder = classPostfix;
566 log.debug( "Artifact does not match the maven name pattern {}", path );
567 info.artifactType = BaseArtifactTypes.UNKNOWN;
568 info.classifier = "";
569 info.remainder = StringUtils.substringAfter( fileName, prefix );
574 log.debug( "Artifact does not match the snapshot version pattern {}", path );
576 info.artifactType = BaseArtifactTypes.UNKNOWN;
577 // This is just a guess. No guarantee to the get a usable version.
578 info.version = StringUtils.removeStart( fileName, info.id + '-' );
579 String postfix = StringUtils.substringAfterLast( info.version, "." ).toLowerCase( );
580 while ( COMMON_EXTENSIONS.matcher( postfix ).matches( ) )
582 info.version = StringUtils.substringBeforeLast( info.version, "." );
583 postfix = StringUtils.substringAfterLast( info.version, "." ).toLowerCase( );
585 info.classifier = "";
586 info.remainder = StringUtils.substringAfter( fileName, prefix );
591 log.debug( "Artifact does not match the maven name pattern: {}", path );
592 if ( fileName.contains( "-" + baseVersion ) )
594 info.id = StringUtils.substringBefore( fileName, "-" + baseVersion );
600 info.artifactType = BaseArtifactTypes.UNKNOWN;
602 info.classifier = "";
603 info.remainder = StringUtils.substringAfterLast( fileName, "." );
608 String prefix = info.id + "-" + genericVersion;
609 if ( fileName.startsWith( prefix ) )
611 info.version = genericVersion;
612 String classPostfix = StringUtils.removeStart( fileName, prefix );
613 Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
614 if ( cMatch.matches( ) )
616 info.classifier = cMatch.group( 1 );
617 info.remainder = cMatch.group( 2 );
621 info.classifier = "";
622 info.remainder = classPostfix;
627 if ( fileName.contains( "-" + genericVersion ) )
629 info.id = StringUtils.substringBefore( fileName, "-" + genericVersion );
635 log.debug( "Artifact does not match the version pattern {}", path );
636 info.artifactType = BaseArtifactTypes.UNKNOWN;
638 info.classifier = "";
639 info.remainder = StringUtils.substringAfterLast( fileName, "." );
642 info.extension = StringUtils.substringAfterLast( fileName, "." );
643 info.type = MavenContentHelper.getTypeFromClassifierAndExtension( info.classifier, info.extension );
646 info.contentType = Files.probeContentType( path.getFilePath( ) );
648 catch ( IOException e )
650 info.contentType = "";
653 if ( MavenContentHelper.METADATA_FILENAME.equalsIgnoreCase( fileName ) )
655 info.artifactType = BaseArtifactTypes.METADATA;
657 else if ( MavenContentHelper.METADATA_REPOSITORY_FILENAME.equalsIgnoreCase( fileName ) )
659 info.artifactType = MavenTypes.REPOSITORY_METADATA;
661 else if ( StringUtils.isNotEmpty( info.remainder ) && StringUtils.countMatches( info.remainder, "." ) >= 2 )
663 String mainFile = StringUtils.substringBeforeLast( fileName, "." );
664 if ( path.getParent( ).resolve( mainFile ).exists( ) )
666 info.artifactType = BaseArtifactTypes.RELATED;
674 public Artifact getArtifact( final ItemSelector selector ) throws ContentAccessException
676 if ( !selector.hasProjectId( ) )
678 throw new IllegalArgumentException( "Project id must be set" );
680 if ( !selector.hasVersion( ) )
682 throw new IllegalArgumentException( "Version must be set" );
684 if ( !selector.hasArtifactId( ) )
686 throw new IllegalArgumentException( "Artifact id must be set" );
688 final StorageAsset artifactDir = getAsset( selector.getNamespace( ), selector.getProjectId( ),
689 selector.getVersion( ) );
690 final String artifactVersion = mavenContentHelper.getArtifactVersion( artifactDir, selector );
691 final String classifier = MavenContentHelper.getClassifier( selector );
692 final String extension = MavenContentHelper.getArtifactExtension( selector );
693 final String artifactId = StringUtils.isEmpty( selector.getArtifactId( ) ) ? selector.getProjectId( ) : selector.getArtifactId( );
694 final String fileName = MavenContentHelper.getArtifactFileName( artifactId, artifactVersion, classifier, extension );
695 final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ),
696 selector.getVersion( ), fileName );
697 return getArtifactFromPath( path );
701 * Returns all the subdirectories of the given namespace directory as project.
704 public List<? extends Project> getProjects( Namespace namespace )
706 return namespace.getAsset( ).list( ).stream( )
707 .filter( a -> a.isContainer( ) )
708 .map( a -> getProjectFromPath( a ) )
709 .collect( Collectors.toList( ) );
713 public List<? extends Project> getProjects( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
715 return getProjects( getNamespace( selector ) );
719 * Returns a version object for each directory that is a direct child of the project directory.
721 * @param project the project for which the versions should be returned
722 * @return the list of versions or a empty list, if not version was found
725 public List<? extends Version> getVersions( final Project project )
727 StorageAsset asset = getAsset( project.getNamespace( ).getNamespace( ), project.getId( ) );
728 return asset.list( ).stream( ).filter( a -> a.isContainer( ) )
729 .map( a -> ArchivaVersion.withAsset( a )
730 .withProject( project )
731 .withVersion( a.getName( ) ).build( ) )
732 .collect( Collectors.toList( ) );
736 * Returns the versions that can be found for the given selector.
738 * @param selector the item selector. At least namespace and projectId must be set.
739 * @return the list of version objects or a empty list, if the selector does not match a version
740 * @throws ContentAccessException if the access to the underlying backend failed
741 * @throws IllegalArgumentException if the selector has no projectId specified
744 public List<? extends Version> getVersions( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
746 if ( !selector.hasProjectId( ) )
748 log.error( "Bad item selector for version list: {}", selector );
749 throw new IllegalArgumentException( "Project id not set, while retrieving versions." );
751 final Project project = getProject( selector );
752 if ( selector.hasVersion( ) )
754 final StorageAsset asset = getAsset( selector.getNamespace( ), selector.getProjectId( ), selector.getVersion( ) );
755 return asset.list( ).stream( ).map( a -> getArtifactInfoFromPath( selector.getVersion( ), a ) )
756 .filter( ai -> StringUtils.isNotEmpty( ai.version ) )
757 .map( v -> getVersionFromArtifactPath( v.asset ) )
759 .collect( Collectors.toList( ) );
763 return getVersions( project );
767 public List<String> getArtifactVersions( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
769 if ( !selector.hasProjectId( ) )
771 log.error( "Bad item selector for version list: {}", selector );
772 throw new IllegalArgumentException( "Project id not set, while retrieving versions." );
774 final Project project = getProject( selector );
775 if ( selector.hasVersion( ) )
777 final StorageAsset asset = getAsset( selector.getNamespace( ), selector.getProjectId( ), selector.getVersion( ) );
778 return asset.list( ).stream( ).map( a -> getArtifactInfoFromPath( selector.getVersion( ), a ) )
779 .filter( ai -> StringUtils.isNotEmpty( ai.version ) )
780 .map( v -> v.version )
782 .collect( Collectors.toList( ) );
786 return project.getAsset( ).list( ).stream( ).map( a -> getVersionFromPath( a ) )
787 .flatMap( v -> v.getAsset( ).list( ).stream( ).map( a -> getArtifactInfoFromPath( v.getVersion( ), a ) ) )
788 .filter( ai -> StringUtils.isNotEmpty( ai.version ) )
789 .map( v -> v.version )
791 .collect( Collectors.toList( ) );
797 * See {@link #newArtifactStream(ItemSelector)}. This method collects the stream into a list.
799 * @param selector the selector for the artifacts
800 * @return the list of artifacts
801 * @throws ContentAccessException if the access to the underlying filesystem failed
804 public List<? extends Artifact> getArtifacts( ItemSelector selector ) throws ContentAccessException
806 try ( Stream<? extends Artifact> stream = newArtifactStream( selector ) )
808 return stream.collect( Collectors.toList( ) );
814 * File filter to select certain artifacts using the selector data.
816 private Predicate<StorageAsset> getArtifactFileFilterFromSelector( final ItemSelector selector )
818 Predicate<StorageAsset> p = a -> a.isLeaf( );
819 StringBuilder fileNamePattern = new StringBuilder( "^" );
820 if ( selector.hasArtifactId( ) )
822 fileNamePattern.append( Pattern.quote( selector.getArtifactId( ) ) ).append( "-" );
826 fileNamePattern.append( "[A-Za-z0-9_\\-.]+-" );
828 if ( selector.hasArtifactVersion( ) )
830 if ( selector.getArtifactVersion( ).contains( "*" ) )
832 String[] tokens = StringUtils.splitByWholeSeparator( selector.getArtifactVersion( ), "*" );
833 for ( String currentToken : tokens )
835 if ( !currentToken.equals( "" ) )
837 fileNamePattern.append( Pattern.quote( currentToken ) );
839 fileNamePattern.append( "[A-Za-z0-9_\\-.]*" );
844 fileNamePattern.append( Pattern.quote( selector.getArtifactVersion( ) ) );
849 fileNamePattern.append( "[A-Za-z0-9_\\-.]+" );
851 String classifier = selector.hasClassifier( ) ? selector.getClassifier( ) :
852 ( selector.hasType( ) ? MavenContentHelper.getClassifierFromType( selector.getType( ) ) : null );
853 if ( classifier != null )
855 if ( "*".equals( classifier ) )
857 fileNamePattern.append( "(-[A-Za-z0-9]+)?\\." );
861 fileNamePattern.append( "-" ).append( Pattern.quote( classifier ) ).append( "\\." );
866 fileNamePattern.append( "\\." );
868 String extension = selector.hasExtension( ) ? selector.getExtension( ) :
869 ( selector.hasType( ) ? MavenContentHelper.getArtifactExtension( selector ) : null );
870 if ( extension != null )
872 if ( selector.includeRelatedArtifacts( ) )
874 fileNamePattern.append( Pattern.quote( extension ) ).append( "(\\.[A-Za-z0-9]+)?" );
878 fileNamePattern.append( Pattern.quote( extension ) );
883 fileNamePattern.append( "[A-Za-z0-9.]+" );
885 final Pattern pattern = Pattern.compile( fileNamePattern.toString( ) );
886 return p.and( a -> pattern.matcher( a.getName( ) ).matches( ) );
891 * Returns the artifacts. The number of artifacts returned depend on the selector.
892 * If the selector sets the flag {@link ItemSelector#includeRelatedArtifacts()} to <code>true</code>,
893 * additional to the matching artifacts, related artifacts like hash values or signatures are included in the artifact
895 * If the selector sets the flag {@link ItemSelector#recurse()} to <code>true</code>, artifacts of the given
896 * namespace and from all sub namespaces that start with the given namespace are returned.
898 * <li>If only a namespace is given, all artifacts with the given namespace or starting with the given
899 * namespace (see {@link ItemSelector#recurse()} are returned.</li>
900 * <li>If a namespace and a project id, or artifact id is given, the artifacts of all versions of the given
901 * namespace and project are returned.</li>
902 * <li>If a namespace and a project id or artifact id and a version is given, the artifacts of the given
903 * version are returned</li>
904 * <li>If no artifact version or artifact id is given, it will return all "artifacts" found in the directory.
905 * To select only artifacts that match the layout you should add the artifact id and artifact version
906 * (can contain a '*' pattern).</li>
909 * The '*' pattern can be used in classifiers and artifact versions and match zero or more characters.
911 * There is no determinate order of the elements in the stream.
913 * Returned streams are auto closable and should be used in a try-with-resources statement.
915 * @param selector the item selector
916 * @throws ContentAccessException if the access to the underlying filesystem failed
919 public Stream<? extends Artifact> newArtifactStream( ItemSelector selector ) throws ContentAccessException
921 String projectId = selector.hasProjectId( ) ? selector.getProjectId( ) : ( selector.hasArtifactId( ) ? selector.getArtifactId( )
923 final Predicate<StorageAsset> filter = getArtifactFileFilterFromSelector( selector );
924 if ( projectId != null && selector.hasVersion( ) )
926 return getAsset( selector.getNamespace( ), projectId, selector.getVersion( ) )
927 .list( ).stream( ).filter( filter )
928 .map( this::getArtifactFromPath );
930 else if ( projectId != null )
932 final StorageAsset projDir = getAsset( selector.getNamespace( ), projectId );
933 return projDir.list( ).stream( )
934 .map( a -> a.isContainer( ) ? a.list( ) : Arrays.asList( a ) )
935 .flatMap( List::stream )
937 .map( this::getArtifactFromPath );
941 StorageAsset namespaceDir = getAsset( selector.getNamespace( ) );
942 if ( selector.recurse( ) )
944 return StorageUtil.newAssetStream( namespaceDir, true )
946 .map( this::getArtifactFromPath );
951 // We descend into 2 subdirectories (project and version)
952 return namespaceDir.list( ).stream( )
953 .map( a -> a.isContainer( ) ? a.list( ) : Arrays.asList( a ) )
954 .flatMap( List::stream )
955 .map( a -> a.isContainer( ) ? a.list( ) : Arrays.asList( a ) )
956 .flatMap( List::stream )
958 .map( this::getArtifactFromPath );
964 * Same as {@link #newArtifactStream(ContentItem)} but returns the collected stream as list.
966 * @param item the item the parent item
967 * @return the list of artifacts or a empty list of no artifacts where found
970 public List<? extends Artifact> getArtifacts( ContentItem item )
972 try ( Stream<? extends Artifact> stream = newArtifactStream( item ) )
974 return stream.collect( Collectors.toList( ) );
979 * Returns all artifacts
983 * @throws ContentAccessException
985 public Stream<? extends Artifact> newArtifactStream( Namespace item ) throws ContentAccessException
987 return newArtifactStream( ArchivaItemSelector.builder( ).withNamespace( item.getNamespace( ) ).build( ) );
990 public Stream<? extends Artifact> newArtifactStream( Project item ) throws ContentAccessException
992 return newArtifactStream( ArchivaItemSelector.builder( ).withNamespace( item.getNamespace( ).getNamespace( ) )
993 .withProjectId( item.getId( ) ).build( ) );
996 public Stream<? extends Artifact> newArtifactStream( Version item ) throws ContentAccessException
998 return newArtifactStream( ArchivaItemSelector.builder( ).withNamespace( item.getProject( ).getNamespace( ).getNamespace( ) )
999 .withProjectId( item.getProject( ).getId( ) )
1000 .withVersion( item.getVersion( ) ).build( ) );
1004 * Returns all related artifacts that match the given artifact. That means all artifacts that have
1005 * the same filename plus an additional extension, e.g. ${fileName}.sha2
1007 * @param item the artifact
1008 * @return the stream of artifacts
1009 * @throws ContentAccessException
1011 public Stream<? extends Artifact> newArtifactStream( Artifact item ) throws ContentAccessException
1013 final Version v = item.getVersion( );
1014 final String fileName = item.getFileName( );
1015 final Predicate<StorageAsset> filter = ( StorageAsset a ) ->
1016 a.getName( ).startsWith( fileName + "." );
1017 return v.getAsset( ).list( ).stream( ).filter( filter )
1018 .map( a -> getArtifactFromPath( a ) );
1022 * Returns the stream of artifacts that are children of the given item.
1024 * @param item the item from where the artifacts should be returned
1026 * @throws ContentAccessException
1029 public Stream<? extends Artifact> newArtifactStream( ContentItem item ) throws ContentAccessException
1031 if ( item instanceof Namespace )
1033 return newArtifactStream( ( (Namespace) item ) );
1035 else if ( item instanceof Project )
1037 return newArtifactStream( (Project) item );
1039 else if ( item instanceof Version )
1041 return newArtifactStream( (Version) item );
1043 else if ( item instanceof Artifact )
1045 return newArtifactStream( (Artifact) item );
1049 log.warn( "newArtifactStream for unsupported item requested: {}", item.getClass( ).getName( ) );
1050 return Stream.empty( );
1054 private void appendPatternRegex( StringBuilder builder, String name )
1056 String[] patternArray = name.split( "[*]" );
1057 for ( int i = 0; i < patternArray.length - 1; i++ )
1059 builder.append( Pattern.quote( patternArray[i] ) )
1060 .append( "[A-Za-z0-9_\\-]*" );
1062 builder.append( Pattern.quote( patternArray[patternArray.length - 1] ) );
1065 Predicate<StorageAsset> getItemFileFilterFromSelector( ItemSelector selector )
1067 if ( !selector.hasNamespace( ) && !selector.hasProjectId( ) )
1069 throw new IllegalArgumentException( "Selector must have at least namespace and projectid" );
1071 StringBuilder pathMatcher = new StringBuilder( "^" );
1072 if ( selector.hasNamespace( ) )
1074 String path = "/" + String.join( "/", selector.getNamespace( ).split( "\\." ) );
1075 if ( path.contains( "*" ) )
1077 appendPatternRegex( pathMatcher, path );
1081 pathMatcher.append( Pattern.quote( path ) );
1085 if ( selector.hasProjectId( ) )
1087 pathMatcher.append( "/" );
1088 if ( selector.getProjectId( ).contains( "*" ) )
1090 appendPatternRegex( pathMatcher, selector.getProjectId( ) );
1094 pathMatcher.append( Pattern.quote( selector.getProjectId( ) ) );
1097 if ( selector.hasVersion( ) )
1099 pathMatcher.append( "/" );
1100 if ( selector.getVersion( ).contains( "*" ) )
1102 appendPatternRegex( pathMatcher, selector.getVersion( ) );
1106 pathMatcher.append( Pattern.quote( selector.getVersion( ) ) );
1109 pathMatcher.append( ".*" );
1110 final Pattern pathPattern = Pattern.compile( pathMatcher.toString( ) );
1111 final Predicate<StorageAsset> pathPredicate = ( StorageAsset asset ) -> pathPattern.matcher( asset.getPath( ) ).matches( );
1112 if ( selector.hasArtifactId( ) || selector.hasArtifactVersion( ) || selector.hasClassifier( )
1113 || selector.hasType( ) || selector.hasExtension( ) )
1115 return getArtifactFileFilterFromSelector( selector ).and( pathPredicate );
1119 return pathPredicate;
1124 * Returns a concatenation of the asset and its children as stream, if they exist.
1125 * It descends <code>level+1</code> levels down.
1127 * @param a the asset to start from
1128 * @param level the number of child levels to descend. 0 means only the children of the given asset, 1 means the children of childrens of the given asset, ...
1129 * @return the stream of storage assets
1131 private Stream<StorageAsset> getChildrenDF( StorageAsset a, int level )
1133 if ( a.isContainer( ) )
1136 return Stream.concat( a.list().stream( ).flatMap( ch -> getChildrenDF( ch, level - 1 ) ), Stream.of( a ) );
1139 return Stream.concat( a.list( ).stream( ), Stream.of( a ) );
1144 return Stream.of( a );
1149 public Stream<? extends ContentItem> newItemStream( ItemSelector selector, boolean parallel ) throws ContentAccessException, IllegalArgumentException
1151 final Predicate<StorageAsset> filter = getItemFileFilterFromSelector( selector );
1152 StorageAsset startDir;
1153 if (selector.getNamespace().contains("*")) {
1154 startDir = getAsset( "" );
1155 } else if ( selector.hasProjectId( ) && selector.getProjectId().contains("*") )
1157 startDir = getAsset( selector.getNamespace( ) );
1158 } else if ( selector.hasProjectId() && selector.hasVersion() && selector.getVersion().contains("*")) {
1159 startDir = getAsset( selector.getNamespace( ), selector.getProjectId( ) );
1161 else if ( selector.hasProjectId( ) && selector.hasVersion( ) )
1163 startDir = getAsset( selector.getNamespace( ), selector.getProjectId( ), selector.getVersion() );
1165 else if ( selector.hasProjectId( ) )
1167 startDir = getAsset( selector.getNamespace( ), selector.getProjectId( ) );
1171 startDir = getAsset( selector.getNamespace( ) );
1172 if ( !selector.recurse( ) )
1174 // We descend into 2 subdirectories (project and version)
1175 return startDir.list( ).stream( )
1176 .flatMap( a -> getChildrenDF( a, 1 ) )
1177 .map( this::getItemFromPath );
1181 return StorageUtil.newAssetStream( startDir, parallel )
1183 .map( this::getItemFromPath );
1188 * Checks, if the asset/file queried by the given selector exists.
1191 public boolean hasContent( ItemSelector selector )
1193 return getItem( selector ).getAsset( ).exists( );
1197 public ContentItem getParent( ContentItem item )
1199 return getItemFromPath( item.getAsset( ).getParent( ) );
1203 public List<? extends ContentItem> getChildren( ContentItem item )
1205 if (item.getAsset().isLeaf()) {
1206 return Collections.emptyList( );
1208 return item.getAsset( ).list( ).stream( ).map( a -> getItemFromPath( a ) ).collect( Collectors.toList( ) );
1213 public <T extends ContentItem> T applyCharacteristic( Class<T> clazz, ContentItem item ) throws LayoutException
1215 if (item.getAsset().isLeaf()) {
1216 if (clazz.isAssignableFrom( Artifact.class )) {
1217 Artifact artifact = getArtifactFromPath( item.getAsset( ) );
1218 item.setCharacteristic( Artifact.class, artifact );
1219 return (T) artifact;
1221 throw new LayoutException( "Could not adapt file to clazz " + clazz );
1224 if (clazz.isAssignableFrom( Version.class )) {
1225 Version version = getVersionFromPath( item.getAsset( ) );
1226 item.setCharacteristic( Version.class, version );
1228 } else if (clazz.isAssignableFrom( Project.class )) {
1229 Project project = getProjectFromPath( item.getAsset( ) );
1230 item.setCharacteristic( Project.class, project );
1232 } else if (clazz.isAssignableFrom( Namespace.class )) {
1233 Namespace ns = getNamespaceFromPath( item.getAsset( ) );
1234 item.setCharacteristic( Namespace.class, ns );
1237 throw new LayoutException( "Cannot adapt directory to clazz " + clazz );
1243 public <T extends ManagedRepositoryContentLayout> T getLayout( Class<T> clazz ) throws LayoutException
1245 if (clazz.isAssignableFrom( this.getClass() )) {
1248 throw new LayoutException( "Cannot convert to layout " + clazz );
1253 public <T extends ManagedRepositoryContentLayout> boolean supportsLayout( Class<T> clazz )
1255 return clazz.isAssignableFrom( this.getClass( ) );
1259 * Moves the file to the artifact destination
1262 public void addArtifact( Path sourceFile, Artifact destination ) throws IllegalArgumentException, ContentAccessException
1266 StorageAsset asset = destination.getAsset( );
1267 if ( !asset.exists( ) )
1271 asset.replaceDataFromFile( sourceFile );
1273 catch ( IOException e )
1275 log.error( "Could not push data to asset source={} destination={}. {}", sourceFile, destination.getAsset( ).getFilePath( ), e.getMessage( ) );
1276 throw new ContentAccessException( e.getMessage( ), e );
1281 public ContentItem toItem( String path ) throws LayoutException
1283 StorageAsset asset = getRepository( ).getAsset( path );
1284 if ( asset.isLeaf( ) )
1286 ItemSelector selector = getPathParser( ).toItemSelector( path );
1287 return getItem( selector );
1291 return getItemFromPath( asset );
1296 public ContentItem toItem( StorageAsset assetPath ) throws LayoutException
1298 return toItem( assetPath.getPath( ) );
1301 /// ************* End of new generation interface ******************
1304 * Returns a version reference from the coordinates
1306 * @param groupId the group id
1307 * @param artifactId the artifact id
1308 * @param version the version
1309 * @return the versioned reference object
1312 public VersionedReference toVersion( String groupId, String artifactId, String version )
1314 return new VersionedReference( ).groupId( groupId ).artifactId( artifactId ).version( version );
1318 * Return the version the artifact is part of
1320 * @param artifactReference
1323 public VersionedReference toVersion( ArtifactReference artifactReference )
1325 return toVersion( artifactReference.getGroupId( ), artifactReference.getArtifactId( ), artifactReference.getVersion( ) );
1329 public String toPath( ContentItem item ) {
1330 return item.getAsset( ).getPath( );
1334 public DataItem getMetadataItem( Version version ) {
1335 StorageAsset metaPath = version.getAsset( ).resolve( MAVEN_METADATA );
1336 return getDataItemFromPath( metaPath );
1341 public void deleteVersion( VersionedReference ref ) throws ContentNotFoundException, ContentAccessException
1343 final String path = toPath( ref );
1344 final Path deleteTarget = getRepoDir( ).resolve( path );
1345 if ( !Files.exists( deleteTarget ) )
1347 log.warn( "Version path for repository {} does not exist: {}", getId( ), deleteTarget );
1348 throw new ContentNotFoundException( "Version not found for repository " + getId( ) + ": " + path );
1350 if ( Files.isDirectory( deleteTarget ) )
1354 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
1356 catch ( IOException e )
1358 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
1359 throw new ContentAccessException( "Error while trying to delete path " + path + " from repository " + getId( ) + ": " + e.getMessage( ), e );
1364 log.warn( "Version path for repository {} is not a directory {}", getId( ), deleteTarget );
1365 throw new ContentNotFoundException( "Version path for repository " + getId( ) + " is not directory: " + path );
1370 public void deleteProject( ProjectReference ref )
1371 throws ContentNotFoundException, ContentAccessException
1373 final String path = toPath( ref );
1374 final Path deleteTarget = getRepoDir( ).resolve( path );
1375 if ( !Files.exists( deleteTarget ) )
1377 log.warn( "Project path for repository {} does not exist: {}", getId( ), deleteTarget );
1378 throw new ContentNotFoundException( "Project not found for repository " + getId( ) + ": " + path );
1380 if ( Files.isDirectory( deleteTarget ) )
1384 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
1386 catch ( IOException e )
1388 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
1389 throw new ContentAccessException( "Error while trying to delete path " + path + " from repository " + getId( ) + ": " + e.getMessage( ), e );
1394 log.warn( "Project path for repository {} is not a directory {}", getId( ), deleteTarget );
1395 throw new ContentNotFoundException( "Project path for repository " + getId( ) + " is not directory: " + path );
1401 public void deleteProject( String namespace, String projectId ) throws ContentNotFoundException, ContentAccessException
1403 this.deleteProject( new ProjectReference( ).groupId( namespace ).artifactId( projectId ) );
1407 public void deleteArtifact( ArtifactReference ref ) throws ContentNotFoundException, ContentAccessException
1409 final String path = toPath( ref );
1410 final Path repoDir = getRepoDir( );
1411 Path deleteTarget = repoDir.resolve( path );
1412 if ( Files.exists( deleteTarget ) )
1416 if ( Files.isDirectory( deleteTarget ) )
1418 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
1422 Files.delete( deleteTarget );
1425 catch ( IOException e )
1427 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
1428 throw new ContentAccessException( "Error while trying to delete path " + path + " from repository " + getId( ) + ": " + e.getMessage( ), e );
1433 log.warn( "Artifact path for repository {} does not exist: {}", getId( ), deleteTarget );
1434 throw new ContentNotFoundException( "Artifact not found for repository " + getId( ) + ": " + path );
1440 public void deleteGroupId( String groupId )
1441 throws ContentNotFoundException, ContentAccessException
1443 final String path = toPath( groupId );
1444 final Path deleteTarget = getRepoDir( ).resolve( path );
1445 if ( !Files.exists( deleteTarget ) )
1447 log.warn( "Namespace path for repository {} does not exist: {}", getId( ), deleteTarget );
1448 throw new ContentNotFoundException( "Namespace not found for repository " + getId( ) + ": " + path );
1450 if ( Files.isDirectory( deleteTarget ) )
1454 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
1456 catch ( IOException e )
1458 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
1459 throw new ContentAccessException( "Error while trying to delete path " + path + " from repository " + getId( ) + ": " + e.getMessage( ), e );
1464 log.warn( "Namespace path for repository {} is not a directory {}", getId( ), deleteTarget );
1465 throw new ContentNotFoundException( "Namespace path for repository " + getId( ) + " is not directory: " + path );
1471 public String getId( )
1473 return repository.getId( );
1477 public List<ArtifactReference> getRelatedArtifacts( VersionedReference reference )
1478 throws ContentNotFoundException, LayoutException, ContentAccessException
1480 StorageAsset artifactDir = toFile( reference );
1481 if ( !artifactDir.exists( ) )
1483 throw new ContentNotFoundException(
1484 "Unable to get related artifacts using a non-existant directory: " + artifactDir.getPath( ) );
1487 if ( !artifactDir.isContainer( ) )
1489 throw new ContentNotFoundException(
1490 "Unable to get related artifacts using a non-directory: " + artifactDir.getPath( ) );
1493 // First gather up the versions found as artifacts in the managed repository.
1495 try ( Stream<? extends StorageAsset> stream = artifactDir.list( ).stream( ) )
1497 return stream.filter( asset -> !asset.isContainer( ) ).map( path -> {
1500 ArtifactReference artifact = toArtifactReference( path.getPath( ) );
1501 if ( artifact.getGroupId( ).equals( reference.getGroupId( ) ) && artifact.getArtifactId( ).equals(
1502 reference.getArtifactId( ) ) && artifact.getVersion( ).equals( reference.getVersion( ) ) )
1511 catch ( LayoutException e )
1513 log.debug( "Not processing file that is not an artifact: {}", e.getMessage( ) );
1516 } ).filter( Objects::nonNull ).collect( Collectors.toList( ) );
1518 catch ( RuntimeException e )
1520 Throwable cause = e.getCause( );
1521 if ( cause != null )
1523 if ( cause instanceof LayoutException )
1525 throw (LayoutException) cause;
1529 throw new ContentAccessException( cause.getMessage( ), cause );
1534 throw new ContentAccessException( e.getMessage( ), e );
1540 * Create the filter for various combinations of classifier and type
1542 private Predicate<ArtifactReference> getChecker( ArtifactReference referenceObject, String extension )
1544 // TODO: Check, if extension is the correct parameter here
1545 // We compare type with extension which works for artifacts like .jar.md5 but may
1546 // be not the best way.
1548 if ( referenceObject.getClassifier( ) != null && referenceObject.getType( ) != null )
1550 return ( ( ArtifactReference a ) ->
1551 referenceObject.getGroupId( ).equals( a.getGroupId( ) )
1552 && referenceObject.getArtifactId( ).equals( a.getArtifactId( ) )
1553 && referenceObject.getVersion( ).equals( a.getVersion( ) )
1554 && ( ( a.getType( ) == null )
1555 || referenceObject.getType( ).equals( a.getType( ) )
1556 || a.getType( ).startsWith( extension ) )
1557 && referenceObject.getClassifier( ).equals( a.getClassifier( ) )
1560 else if ( referenceObject.getClassifier( ) != null && referenceObject.getType( ) == null )
1562 return ( ( ArtifactReference a ) ->
1563 referenceObject.getGroupId( ).equals( a.getGroupId( ) )
1564 && referenceObject.getArtifactId( ).equals( a.getArtifactId( ) )
1565 && referenceObject.getVersion( ).equals( a.getVersion( ) )
1566 && referenceObject.getClassifier( ).equals( a.getClassifier( ) )
1569 else if ( referenceObject.getClassifier( ) == null && referenceObject.getType( ) != null )
1571 return ( ( ArtifactReference a ) ->
1572 referenceObject.getGroupId( ).equals( a.getGroupId( ) )
1573 && referenceObject.getArtifactId( ).equals( a.getArtifactId( ) )
1574 && referenceObject.getVersion( ).equals( a.getVersion( ) )
1575 && ( ( a.getType( ) == null )
1576 || referenceObject.getType( ).equals( a.getType( ) )
1577 || a.getType( ).startsWith( extension ) )
1582 return ( ( ArtifactReference a ) ->
1583 referenceObject.getGroupId( ).equals( a.getGroupId( ) )
1584 && referenceObject.getArtifactId( ).equals( a.getArtifactId( ) )
1585 && referenceObject.getVersion( ).equals( a.getVersion( ) )
1593 public String getRepoRoot( )
1595 return convertUriToPath( repository.getLocation( ) );
1598 private String convertUriToPath( URI uri )
1600 if ( uri.getScheme( ) == null )
1602 return Paths.get( uri.getPath( ) ).toString( );
1604 else if ( "file".equals( uri.getScheme( ) ) )
1606 return Paths.get( uri ).toString( );
1610 return uri.toString( );
1615 public ManagedRepository getRepository( )
1621 public boolean hasContent( ArtifactReference reference ) throws ContentAccessException
1623 StorageAsset artifactFile = toFile( reference );
1624 return artifactFile.exists( ) && !artifactFile.isContainer( );
1628 public boolean hasContent( VersionedReference reference ) throws ContentAccessException
1632 return ( getFirstArtifact( reference ) != null );
1634 catch ( LayoutException | ContentNotFoundException e )
1638 catch ( IOException e )
1640 String path = toPath( reference );
1641 log.error( "Could not read directory from repository {} - {}: ", getId( ), path, e.getMessage( ), e );
1642 throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
1647 public void setRepository( final ManagedRepository repo )
1649 this.repository = repo;
1652 if ( repository instanceof EditableManagedRepository )
1654 ( (EditableManagedRepository) repository ).setContent( this );
1659 private Path getRepoDir( )
1661 return repository.getAsset( "" ).getFilePath( );
1664 private RepositoryStorage getStorage( )
1666 return repository.getAsset( "" ).getStorage( );
1670 * Convert a path to an artifact reference.
1672 * @param path the path to convert. (relative or full location path)
1673 * @throws LayoutException if the path cannot be converted to an artifact reference.
1676 public ArtifactReference toArtifactReference( String path )
1677 throws LayoutException
1679 String repoPath = convertUriToPath( repository.getLocation( ) );
1680 if ( ( path != null ) && path.startsWith( repoPath ) && repoPath.length( ) > 0 )
1682 return super.toArtifactReference( path.substring( repoPath.length( ) + 1 ) );
1687 if ( repoPath != null )
1689 while ( repoPath.startsWith( "/" ) )
1691 repoPath = repoPath.substring( 1 );
1694 return super.toArtifactReference( repoPath );
1699 // The variant with runtime exception for stream usage
1700 private ArtifactReference toArtifactRef( String path )
1704 return toArtifactReference( path );
1706 catch ( LayoutException e )
1708 throw new RuntimeException( e );
1714 public StorageAsset toFile( ArtifactReference reference )
1716 return repository.getAsset( toPath( reference ) );
1720 public StorageAsset toFile( ArchivaArtifact reference )
1722 return repository.getAsset( toPath( reference ) );
1726 public StorageAsset toFile( VersionedReference reference )
1728 return repository.getAsset( toPath( reference ) );
1732 * Get the first Artifact found in the provided VersionedReference location.
1734 * @param reference the reference to the versioned reference to search within
1735 * @return the ArtifactReference to the first artifact located within the versioned reference. or null if
1736 * no artifact was found within the versioned reference.
1737 * @throws java.io.IOException if the versioned reference is invalid (example: doesn't exist, or isn't a directory)
1738 * @throws LayoutException
1740 private ArtifactReference getFirstArtifact( VersionedReference reference )
1741 throws ContentNotFoundException, LayoutException, IOException
1743 try ( Stream<ArtifactReference> stream = newArtifactStream( reference ) )
1745 return stream.findFirst( ).orElse( null );
1747 catch ( RuntimeException e )
1749 throw new ContentNotFoundException( e.getMessage( ), e.getCause( ) );
1753 private Stream<ArtifactReference> newArtifactStream( VersionedReference reference ) throws ContentNotFoundException, LayoutException, IOException
1755 final Path repoBase = getRepoDir( );
1756 String path = toMetadataPath( reference );
1757 Path versionDir = repoBase.resolve( path ).getParent( );
1758 if ( !Files.exists( versionDir ) )
1760 throw new ContentNotFoundException( "Unable to gather the list of artifacts on a non-existant directory: "
1761 + versionDir.toAbsolutePath( ) );
1764 if ( !Files.isDirectory( versionDir ) )
1766 throw new ContentNotFoundException(
1767 "Unable to gather the list of snapshot versions on a non-directory: " + versionDir.toAbsolutePath( ) );
1769 return Files.list( versionDir ).filter( Files::isRegularFile )
1770 .map( p -> repoBase.relativize( p ).toString( ) )
1771 .filter( p -> !filetypes.matchesDefaultExclusions( p ) )
1772 .filter( filetypes::matchesArtifactPattern )
1773 .map( this::toArtifactRef );
1776 public List<ArtifactReference> getArtifacts( VersionedReference reference ) throws ContentNotFoundException, LayoutException, ContentAccessException
1778 try ( Stream<ArtifactReference> stream = newArtifactStream( reference ) )
1780 return stream.collect( Collectors.toList( ) );
1782 catch ( IOException e )
1784 String path = toPath( reference );
1785 log.error( "Could not read directory from repository {} - {}: ", getId( ), path, e.getMessage( ), e );
1786 throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
1791 private boolean hasArtifact( VersionedReference reference )
1794 try ( Stream<ArtifactReference> stream = newArtifactStream( reference ) )
1796 return stream.anyMatch( e -> true );
1798 catch ( ContentNotFoundException e )
1802 catch ( LayoutException | IOException e )
1804 // We throw the runtime exception for better stream handling
1805 throw new RuntimeException( e );
1809 public void setFiletypes( FileTypes filetypes )
1811 this.filetypes = filetypes;
1814 public void setMavenContentHelper( MavenContentHelper contentHelper )
1816 this.mavenContentHelper = contentHelper;
1820 public MavenMetadataReader getMetadataReader( )
1822 return metadataReader;
1825 public void setMetadataReader( MavenMetadataReader metadataReader )
1827 this.metadataReader = metadataReader;