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.LayoutException;
35 import org.apache.archiva.repository.ManagedRepository;
36 import org.apache.archiva.repository.ManagedRepositoryContent;
37 import org.apache.archiva.repository.content.Artifact;
38 import org.apache.archiva.repository.content.ArtifactType;
39 import org.apache.archiva.repository.content.ContentItem;
40 import org.apache.archiva.repository.content.ItemNotFoundException;
41 import org.apache.archiva.repository.content.ItemSelector;
42 import org.apache.archiva.repository.content.BaseArtifactTypes;
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.ArchivaItemSelector;
47 import org.apache.archiva.repository.content.base.ArchivaNamespace;
48 import org.apache.archiva.repository.content.base.ArchivaProject;
49 import org.apache.archiva.repository.content.base.ArchivaVersion;
50 import org.apache.archiva.repository.content.base.builder.ArtifactOptBuilder;
51 import org.apache.archiva.repository.maven.metadata.storage.ArtifactMappingProvider;
52 import org.apache.archiva.repository.maven.metadata.storage.DefaultArtifactMappingProvider;
53 import org.apache.archiva.repository.storage.RepositoryStorage;
54 import org.apache.archiva.repository.storage.StorageAsset;
55 import org.apache.archiva.repository.storage.util.StorageUtil;
56 import org.apache.commons.collections4.map.ReferenceMap;
57 import org.apache.commons.lang3.StringUtils;
59 import javax.inject.Inject;
60 import javax.inject.Named;
61 import java.io.IOException;
63 import java.nio.file.Files;
64 import java.nio.file.Path;
65 import java.nio.file.Paths;
66 import java.util.Arrays;
67 import java.util.Collections;
68 import java.util.List;
69 import java.util.Objects;
70 import java.util.Optional;
72 import java.util.function.Predicate;
73 import java.util.regex.Matcher;
74 import java.util.regex.Pattern;
75 import java.util.stream.Collectors;
76 import java.util.stream.Stream;
79 * ManagedDefaultRepositoryContent
81 public class ManagedDefaultRepositoryContent
82 extends AbstractDefaultRepositoryContent
83 implements ManagedRepositoryContent
86 // attribute flag that marks version objects that point to a snapshot artifact version
87 public static final String SNAPSHOT_ARTIFACT_VERSION = "maven.snav";
89 private FileTypes filetypes;
91 public void setFileTypes(FileTypes fileTypes) {
92 this.filetypes = fileTypes;
95 private ManagedRepository repository;
97 private FileLockManager lockManager;
100 @Named("repositoryPathTranslator#maven2")
101 private RepositoryPathTranslator pathTranslator;
104 @Named( "metadataReader#maven" )
105 MavenMetadataReader metadataReader;
108 @Named( "MavenContentHelper" )
109 MavenContentHelper mavenContentHelper;
111 public static final String SNAPSHOT = "SNAPSHOT";
113 public static final Pattern UNIQUE_SNAPSHOT_PATTERN = Pattern.compile( "^(SNAPSHOT|[0-9]{8}\\.[0-9]{6}-[0-9]+)(.*)" );
114 public static final Pattern CLASSIFIER_PATTERN = Pattern.compile( "^-([^.]+)(\\..*)" );
115 public static final Pattern COMMON_EXTENSIONS = Pattern.compile( "^(jar|war|ear|dar|tar|zip|pom|xml)$" );
117 public static final Pattern TIMESTAMP_PATTERN = Pattern.compile( "^([0-9]{8})\\.([0-9]{6})$" );
119 public static final Pattern GENERIC_SNAPSHOT_PATTERN = Pattern.compile( "^(.*)-" + SNAPSHOT );
122 * We are caching content items in a weak reference map. To avoid always recreating the
123 * the hierarchical structure.
124 * TODO: Better use a object cache? E.g. our spring cache implementation?
126 private ReferenceMap<String, Namespace> namespaceMap = new ReferenceMap<>( );
127 private ReferenceMap<StorageAsset, Project> projectMap = new ReferenceMap<>( );
128 private ReferenceMap<StorageAsset, Version> versionMap = new ReferenceMap<>( );
129 private ReferenceMap<StorageAsset, Artifact> artifactMap = new ReferenceMap<>( );
131 public ManagedDefaultRepositoryContent() {
132 super(Collections.singletonList( new DefaultArtifactMappingProvider() ));
135 public ManagedDefaultRepositoryContent(ManagedRepository repository, FileTypes fileTypes, FileLockManager lockManager) {
136 super(Collections.singletonList( new DefaultArtifactMappingProvider() ));
137 setFileTypes( fileTypes );
138 this.lockManager = lockManager;
139 setRepository( repository );
142 public ManagedDefaultRepositoryContent( ManagedRepository repository, List<? extends ArtifactMappingProvider> artifactMappingProviders, FileTypes fileTypes, FileLockManager lockManager )
144 super(artifactMappingProviders==null ? Collections.singletonList( new DefaultArtifactMappingProvider() ) : artifactMappingProviders);
145 setFileTypes( fileTypes );
146 this.lockManager = lockManager;
147 setRepository( repository );
151 private StorageAsset getAssetByPath(String assetPath) {
152 return getStorage( ).getAsset( assetPath );
155 private StorageAsset getAsset(String namespace) {
156 String namespacePath = formatAsDirectory( namespace.trim() );
157 if (StringUtils.isEmpty( namespacePath )) {
160 return getAssetByPath(namespacePath);
163 private StorageAsset getAsset(String namespace, String project) {
164 return getAsset( namespace ).resolve( project );
167 private StorageAsset getAsset(String namespace, String project, String version) {
168 return getAsset( namespace, project ).resolve( version );
171 private StorageAsset getAsset(String namespace, String project, String version, String fileName) {
172 return getAsset( namespace, project, version ).resolve( fileName );
176 /// ************* End of new generation interface ******************
179 * Removes the item from the filesystem. For namespaces, projects and versions it deletes
181 * For namespaces you have to be careful, because maven repositories may have sub namespaces
182 * parallel to projects. Which means deleting a namespaces also deletes the sub namespaces and
183 * not only the projects of the given namespace. Better run the delete for each project of
186 * Artifacts are deleted as provided. No related artifacts will be deleted.
188 * @param item the item that should be removed
189 * @throws ItemNotFoundException if the item does not exist
190 * @throws ContentAccessException if some error occurred while accessing the filesystem
193 public void deleteItem( ContentItem item ) throws ItemNotFoundException, ContentAccessException
195 final Path baseDirectory = getRepoDir( );
196 final Path itemPath = item.getAsset( ).getFilePath( );
197 if ( !Files.exists( itemPath ) )
199 throw new ItemNotFoundException( "The item " + item.toString() + "does not exist in the repository " + getId( ) );
201 if ( !itemPath.toAbsolutePath().startsWith( baseDirectory.toAbsolutePath() ) )
203 log.error( "The namespace {} to delete from repository {} is not a subdirectory of the repository base.", item, getId( ) );
204 log.error( "Namespace directory: {}", itemPath );
205 log.error( "Repository directory: {}", baseDirectory );
206 throw new ContentAccessException( "Inconsistent directories found. Could not delete namespace." );
210 if (Files.isDirectory( itemPath ))
212 FileUtils.deleteDirectory( itemPath );
214 Files.deleteIfExists( itemPath );
217 catch ( IOException e )
219 log.error( "Could not delete item from path {}: {}", itemPath, e.getMessage( ), e );
220 throw new ContentAccessException( "Error occured while deleting item " + item + ": " + e.getMessage( ), e );
225 public ContentItem getItem( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
227 if (selector.hasVersion() && selector.hasArtifactId()) {
228 return getArtifact( selector );
229 } else if (selector.hasProjectId() && selector.hasVersion()) {
230 return getVersion( selector );
231 } else if (selector.hasProjectId()) {
232 return getProject( selector );
234 return getNamespace( selector );
239 public Namespace getNamespace( final ItemSelector namespaceSelector ) throws ContentAccessException, IllegalArgumentException
241 return namespaceMap.computeIfAbsent( namespaceSelector.getNamespace(),
243 StorageAsset nsPath = getAsset( namespace );
244 return ArchivaNamespace.withRepository( this ).withAsset( nsPath ).
245 withNamespace( namespace ).build( );
251 public Project getProject( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
253 if (!selector.hasProjectId()) {
254 throw new IllegalArgumentException( "Project id must be set" );
256 final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ) );
257 return projectMap.computeIfAbsent( path, projectPath -> {
258 final Namespace ns = getNamespace( selector );
259 return ArchivaProject.withAsset( projectPath ).withNamespace( ns ).withId( selector.getProjectId( ) ).build( );
266 public Version getVersion( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
268 if (!selector.hasProjectId()) {
269 throw new IllegalArgumentException( "Project id must be set" );
271 if (!selector.hasVersion() ) {
272 throw new IllegalArgumentException( "Version must be set" );
274 final StorageAsset path = getAsset(selector.getNamespace(), selector.getProjectId(), selector.getVersion());
275 return versionMap.computeIfAbsent( path, versionPath -> {
276 final Project project = getProject( selector );
277 return ArchivaVersion.withAsset( path )
278 .withProject( project )
279 .withVersion( selector.getVersion( ) ).build();
287 public Artifact createArtifact(final StorageAsset artifactPath, final ItemSelector selector,
288 final String classifier, final String extension) {
289 Version version = getVersion(selector);
290 ArtifactOptBuilder builder = org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
291 .withVersion( version )
292 .withId( selector.getArtifactId( ) )
293 .withArtifactVersion( mavenContentHelper.getArtifactVersion( artifactPath, selector ) )
294 .withClassifier( classifier );
295 if (selector.hasType()) {
296 builder.withType( selector.getType( ) );
298 return builder.build( );
301 public Namespace getNamespaceFromArtifactPath( final StorageAsset artifactPath) {
302 final StorageAsset namespacePath = artifactPath.getParent( ).getParent( ).getParent( );
303 final String namespace = MavenContentHelper.getNamespaceFromNamespacePath( namespacePath );
304 return namespaceMap.computeIfAbsent( namespace,
305 myNamespace -> ArchivaNamespace.withRepository( this )
306 .withAsset( namespacePath )
307 .withNamespace( namespace )
311 public Namespace getNamespaceFromPath( final StorageAsset namespacePath) {
312 final String namespace = MavenContentHelper.getNamespaceFromNamespacePath( namespacePath );
313 return namespaceMap.computeIfAbsent( namespace,
314 myNamespace -> ArchivaNamespace.withRepository( this )
315 .withAsset( namespacePath )
316 .withNamespace( namespace )
320 private Project getProjectFromPath( final StorageAsset projectPath) {
321 return projectMap.computeIfAbsent( projectPath,
322 myProjectPath -> ArchivaProject.withAsset( projectPath )
323 .withNamespace( getNamespaceFromPath( projectPath.getParent() ) )
324 .withId( projectPath.getName( ) ).build( )
328 private Project getProjectFromArtifactPath( final StorageAsset artifactPath) {
329 final StorageAsset projectPath = artifactPath.getParent( ).getParent( );
330 return projectMap.computeIfAbsent( projectPath,
331 myProjectPath -> ArchivaProject.withAsset( projectPath )
332 .withNamespace( getNamespaceFromArtifactPath( artifactPath ) )
333 .withId( projectPath.getName( ) ).build( )
337 private Version getVersionFromArtifactPath( final StorageAsset artifactPath) {
338 final StorageAsset versionPath = artifactPath.getParent( );
339 return versionMap.computeIfAbsent( versionPath,
340 myVersionPath -> ArchivaVersion.withAsset( versionPath )
341 .withProject( getProjectFromArtifactPath( artifactPath ) )
342 .withVersion( versionPath.getName( ) ).build( ) );
345 private Artifact getArtifactFromPath(final StorageAsset artifactPath) {
346 final Version version = getVersionFromArtifactPath( artifactPath );
347 final ArtifactInfo info = getArtifactInfoFromPath( version.getVersion(), artifactPath );
348 return artifactMap.computeIfAbsent( artifactPath, myArtifactPath ->
349 org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
350 .withVersion( version )
352 .withClassifier( info.classifier )
353 .withRemainder( info.remainder )
354 .withType( info.type )
355 .withArtifactVersion( info.version )
356 .withContentType( info.contentType )
357 .withArtifactType( info.artifactType )
362 private ContentItem getItemFromPath(final StorageAsset itemPath) {
363 if (itemPath.isLeaf()) {
364 return getArtifactFromPath( itemPath );
366 if (versionMap.containsKey( itemPath )) {
367 return versionMap.get( itemPath );
369 if (projectMap.containsKey( itemPath )) {
370 return projectMap.get( itemPath );
372 String ns = MavenContentHelper.getNamespaceFromNamespacePath( itemPath );
373 if (namespaceMap.containsKey( ns )) {
374 return namespaceMap.get( ns );
376 // No cached item, so we have to gather more information:
377 // Check for version directory (contains at least a pom or metadata file)
378 if (itemPath.list( ).stream( ).map(a -> a.getName().toLowerCase()).anyMatch( n ->
381 return versionMap.computeIfAbsent( itemPath,
382 myVersionPath -> ArchivaVersion.withAsset( itemPath )
383 .withProject( (Project)getItemFromPath( itemPath.getParent() ) )
384 .withVersion( itemPath.getName() ).build());
386 // We have to dig further and find the next directory with a pom
387 Optional<StorageAsset> foundFile = StorageUtil.newAssetStream( itemPath )
388 .filter( a -> a.getName().toLowerCase().endsWith( ".pom" )
389 || a.getName().toLowerCase().startsWith( "maven-metadata" ) )
391 if (foundFile.isPresent())
394 StorageAsset current = foundFile.get( );
395 while (current.hasParent() && !current.equals(itemPath)) {
397 current = current.getParent( );
399 // Project path if it is one level up from the found file
401 return projectMap.computeIfAbsent( itemPath,
402 myItemPath -> getProjectFromArtifactPath( foundFile.get( ) ) );
404 // All other paths are treated as namespace
405 return namespaceMap.computeIfAbsent( ns,
406 myNamespace -> ArchivaNamespace.withRepository( this )
407 .withAsset( itemPath )
412 // Don't know what to do with it, so we treat it as namespace path
413 return namespaceMap.computeIfAbsent( ns,
414 myNamespace -> ArchivaNamespace.withRepository( this )
415 .withAsset( itemPath )
424 // Simple object to hold artifact information
425 private class ArtifactInfo {
427 private String version;
428 private String extension;
429 private String remainder;
431 private String classifier;
432 private String contentType;
433 private StorageAsset asset;
434 private ArtifactType artifactType = BaseArtifactTypes.MAIN;
437 private ArtifactInfo getArtifactInfoFromPath(String genericVersion, StorageAsset path) {
438 final ArtifactInfo info = new ArtifactInfo( );
440 info.id = path.getParent( ).getParent( ).getName( );
441 final String fileName = path.getName( );
442 if ( genericVersion.endsWith( "-" + SNAPSHOT ) )
444 String baseVersion = StringUtils.substringBeforeLast( genericVersion, "-" + SNAPSHOT );
445 String prefix = info.id+"-"+baseVersion+"-";
446 if (fileName.startsWith( prefix ))
448 String versionPostfix = StringUtils.removeStart( fileName, prefix );
449 Matcher matcher = UNIQUE_SNAPSHOT_PATTERN.matcher( versionPostfix );
450 if (matcher.matches()) {
451 info.version = baseVersion + "-" + matcher.group( 1 );
452 String newPrefix = info.id + "-" + info.version;
453 if (fileName.startsWith( newPrefix ))
455 String classPostfix = StringUtils.removeStart( fileName, newPrefix );
456 Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
457 if (cMatch.matches()) {
458 info.classifier = cMatch.group( 1 );
459 info.remainder = cMatch.group( 2 );
461 info.classifier = "";
462 info.remainder = classPostfix;
465 log.debug( "Artifact does not match the maven name pattern {}", path );
466 info.artifactType = BaseArtifactTypes.UNKNOWN;
467 info.classifier = "";
468 info.remainder = StringUtils.substringAfter( fileName, prefix );
471 log.debug( "Artifact does not match the snapshot version pattern {}", path );
473 info.artifactType = BaseArtifactTypes.UNKNOWN;
474 // This is just a guess. No guarantee to the get a usable version.
475 info.version = StringUtils.removeStart( fileName, info.id + '-' );
476 String postfix = StringUtils.substringAfterLast( info.version, "." ).toLowerCase();
477 while (COMMON_EXTENSIONS.matcher(postfix).matches()) {
478 info.version = StringUtils.substringBeforeLast( info.version, "." );
479 postfix = StringUtils.substringAfterLast( info.version, "." ).toLowerCase();
481 info.classifier = "";
482 info.remainder = StringUtils.substringAfter( fileName, prefix );
485 log.debug( "Artifact does not match the maven name pattern: {}", path );
486 if ( fileName.contains( "-"+baseVersion ) )
488 info.id = StringUtils.substringBefore( fileName, "-"+baseVersion );
492 info.artifactType = BaseArtifactTypes.UNKNOWN;
494 info.classifier = "";
495 info.remainder = StringUtils.substringAfterLast( fileName, "." );
498 String prefix = info.id+"-"+genericVersion;
499 if (fileName.startsWith( prefix ))
501 info.version=genericVersion;
502 String classPostfix = StringUtils.removeStart( fileName, prefix );
503 Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
504 if (cMatch.matches()) {
505 info.classifier = cMatch.group( 1 );
506 info.remainder = cMatch.group( 2 );
508 info.classifier = "";
509 info.remainder = classPostfix;
512 if (fileName.contains( "-"+genericVersion )) {
513 info.id = StringUtils.substringBefore( fileName, "-"+genericVersion );
517 log.debug( "Artifact does not match the version pattern {}", path );
518 info.artifactType = BaseArtifactTypes.UNKNOWN;
520 info.classifier = "";
521 info.remainder = StringUtils.substringAfterLast( fileName, "." );
524 info.extension = StringUtils.substringAfterLast( fileName, "." );
525 info.type = MavenContentHelper.getTypeFromClassifierAndExtension( info.classifier, info.extension );
527 info.contentType = Files.probeContentType( path.getFilePath( ) );
528 } catch (IOException e) {
529 info.contentType = "";
532 if (MavenContentHelper.METADATA_FILENAME.equalsIgnoreCase( fileName )) {
533 info.artifactType = BaseArtifactTypes.METADATA;
534 } else if (MavenContentHelper.METADATA_REPOSITORY_FILENAME.equalsIgnoreCase( fileName )) {
535 info.artifactType = MavenTypes.REPOSITORY_METADATA;
536 } else if (StringUtils.isNotEmpty( info.remainder ) && StringUtils.countMatches( info.remainder, "." )>=2) {
537 String mainFile = StringUtils.substringBeforeLast( fileName, "." );
538 if (path.getParent().resolve( mainFile ).exists())
540 info.artifactType = BaseArtifactTypes.RELATED;
548 public Artifact getArtifact( final ItemSelector selector ) throws ContentAccessException
550 if (!selector.hasProjectId( )) {
551 throw new IllegalArgumentException( "Project id must be set" );
553 if (!selector.hasVersion( )) {
554 throw new IllegalArgumentException( "Version must be set" );
556 if (!selector.hasArtifactId( )) {
557 throw new IllegalArgumentException( "Artifact id must be set" );
559 final StorageAsset artifactDir = getAsset(selector.getNamespace(), selector.getProjectId(),
560 selector.getVersion());
561 final String artifactVersion = mavenContentHelper.getArtifactVersion( artifactDir, selector );
562 final String classifier = MavenContentHelper.getClassifier( selector );
563 final String extension = MavenContentHelper.getArtifactExtension( selector );
564 final String artifactId = StringUtils.isEmpty( selector.getArtifactId( ) ) ? selector.getProjectId( ) : selector.getArtifactId( );
565 final String fileName = MavenContentHelper.getArtifactFileName( artifactId, artifactVersion, classifier, extension );
566 final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ),
567 selector.getVersion( ), fileName );
568 return artifactMap.computeIfAbsent( path, artifactPath -> createArtifact( path, selector, classifier, extension ) );
572 * Returns all the subdirectories of the given namespace directory as project.
575 public List<? extends Project> getProjects( Namespace namespace )
577 return namespace.getAsset( ).list( ).stream( )
578 .filter( a -> a.isContainer( ) )
579 .map( a -> getProjectFromPath( a ) )
580 .collect( Collectors.toList());
584 public List<? extends Project> getProjects( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
586 return getProjects( getNamespace( selector ) );
590 * Returns a version object for each directory that is a direct child of the project directory.
591 * @param project the project for which the versions should be returned
592 * @return the list of versions or a empty list, if not version was found
595 public List<? extends Version> getVersions( final Project project )
597 StorageAsset asset = getAsset( project.getNamespace( ).getNamespace( ), project.getId( ) );
598 return asset.list( ).stream( ).filter( a -> a.isContainer( ) )
599 .map( a -> ArchivaVersion.withAsset( a )
600 .withProject( project )
601 .withVersion( a.getName() ).build() )
602 .collect( Collectors.toList( ) );
606 * If the selector specifies a version, all artifact versions are returned, which means for snapshot
607 * versions the artifact versions are returned too.
609 * @param selector the item selector. At least namespace and projectId must be set.
610 * @return the list of version objects or a empty list, if the selector does not match a version
611 * @throws ContentAccessException if the access to the underlying backend failed
612 * @throws IllegalArgumentException if the selector has no projectId specified
615 public List<? extends Version> getVersions( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
617 if (!selector.hasProjectId()) {
618 log.error( "Bad item selector for version list: {}", selector );
619 throw new IllegalArgumentException( "Project id not set, while retrieving versions." );
621 final Project project = getProject( selector );
622 if (selector.hasVersion()) {
623 final StorageAsset asset = getAsset( selector.getNamespace( ), selector.getProjectId( ), selector.getVersion( ) );
624 return asset.list( ).stream( ).map( a -> getArtifactInfoFromPath( selector.getVersion( ), a ) )
625 .filter( ai -> StringUtils.isNotEmpty( ai.version ) )
626 .map( v -> ArchivaVersion.withAsset( v.asset.getParent() )
627 .withProject( project ).withVersion( v.version )
628 .withAttribute(SNAPSHOT_ARTIFACT_VERSION,"true").build() )
630 .collect( Collectors.toList( ) );
632 return getVersions( project );
638 * See {@link #newArtifactStream(ItemSelector)}. This method collects the stream into a list.
640 * @param selector the selector for the artifacts
641 * @return the list of artifacts
642 * @throws ContentAccessException if the access to the underlying filesystem failed
645 public List<? extends Artifact> getArtifacts( ItemSelector selector ) throws ContentAccessException
647 try(Stream<? extends Artifact> stream = newArtifactStream( selector )) {
648 return stream.collect( Collectors.toList());
654 * File filter to select certain artifacts using the selector data.
656 private Predicate<StorageAsset> getFileFilterFromSelector(final ItemSelector selector) {
657 Predicate<StorageAsset> p = a -> a.isLeaf( );
658 StringBuilder fileNamePattern = new StringBuilder("^" );
659 if (selector.hasArtifactId()) {
660 fileNamePattern.append( Pattern.quote(selector.getArtifactId( )) ).append("-");
662 fileNamePattern.append("[A-Za-z0-9_\\-.]+-");
664 if (selector.hasArtifactVersion()) {
665 if ( selector.getArtifactVersion( ).contains("*")) {
666 String[] tokens = StringUtils.splitByWholeSeparator( selector.getArtifactVersion( ), "*" );
667 for (String currentToken : tokens) {
668 if (!currentToken.equals("")) {
669 fileNamePattern.append( Pattern.quote( currentToken ) );
671 fileNamePattern.append( "[A-Za-z0-9_\\-.]*" );
675 fileNamePattern.append( Pattern.quote( selector.getArtifactVersion( ) ) );
678 fileNamePattern.append( "[A-Za-z0-9_\\-.]+" );
680 String classifier = selector.hasClassifier( ) ? selector.getClassifier( ) :
681 ( selector.hasType( ) ? MavenContentHelper.getClassifierFromType( selector.getType( ) ) : null );
682 if (classifier != null)
684 if ( "*".equals( classifier ) )
686 fileNamePattern.append( "(-[A-Za-z0-9]+)?\\." );
690 fileNamePattern.append("-").append( Pattern.quote( classifier ) ).append( "\\." );
693 fileNamePattern.append( "\\." );
695 String extension = selector.hasExtension( ) ? selector.getExtension( ) :
696 ( selector.hasType( ) ? MavenContentHelper.getArtifactExtension( selector ) : null );
697 if (extension != null) {
698 if (selector.includeRelatedArtifacts())
700 fileNamePattern.append( Pattern.quote( extension ) ).append("(\\.[A-Za-z0-9]+)?");
702 fileNamePattern.append( Pattern.quote( extension ) );
705 fileNamePattern.append( "[A-Za-z0-9]+" );
707 final Pattern pattern = Pattern.compile( fileNamePattern.toString() );
708 return p.and( a -> pattern.matcher( a.getName( ) ).matches());
713 * Returns the artifacts. The number of artifacts returned depend on the selector.
714 * If the selector sets the flag {@link ItemSelector#includeRelatedArtifacts()} to <code>true</code>,
715 * additional to the matching artifacts, related artifacts like hash values or signatures are included in the artifact
717 * If the selector sets the flag {@link ItemSelector#recurse()} to <code>true</code>, artifacts of the given
718 * namespace and from all sub namespaces that start with the given namespace are returned.
720 * <li>If only a namespace is given, all artifacts with the given namespace or starting with the given
721 * namespace (see {@link ItemSelector#recurse()} are returned.</li>
722 * <li>If a namespace and a project id, or artifact id is given, the artifacts of all versions of the given
723 * namespace and project are returned.</li>
724 * <li>If a namespace and a project id or artifact id and a version is given, the artifacts of the given
725 * version are returned</li>
726 * <li>If no artifact version or artifact id is given, it will return all "artifacts" found in the directory.
727 * To select only artifacts that match the layout you should add the artifact id and artifact version
728 * (can contain a '*' pattern).</li>
731 * The '*' pattern can be used in classifiers and artifact versions and match zero or more characters.
733 * There is no determinate order of the elements in the stream.
735 * Returned streams are auto closable and should be used in a try-with-resources statement.
737 * @param selector the item selector
738 * @throws ContentAccessException if the access to the underlying filesystem failed
741 public Stream<? extends Artifact> newArtifactStream( ItemSelector selector ) throws ContentAccessException
743 String projectId = selector.hasProjectId( ) ? selector.getProjectId( ) : ( selector.hasArtifactId( ) ? selector.getArtifactId( )
745 final Predicate<StorageAsset> filter = getFileFilterFromSelector( selector );
746 if (projectId!=null && selector.hasVersion()) {
747 return getAsset( selector.getNamespace( ), projectId, selector.getVersion( ) )
748 .list( ).stream( ).filter( filter )
749 .map( this::getArtifactFromPath );
750 } else if (projectId!=null) {
751 final StorageAsset projDir = getAsset( selector.getNamespace( ), projectId );
752 return projDir.list( ).stream( )
753 .map(a -> a.isContainer( ) ? a.list( ) : Arrays.asList( a ) )
754 .flatMap( List::stream )
756 .map( this::getArtifactFromPath );
759 StorageAsset namespaceDir = getAsset( selector.getNamespace( ) );
760 if (selector.recurse())
762 return StorageUtil.newAssetStream( namespaceDir, true )
764 .map( this::getArtifactFromPath );
767 // We descend into 2 subdirectories (project and version)
768 return namespaceDir.list( ).stream( )
769 .map( a -> a.isContainer( ) ? a.list( ) : Arrays.asList( a ) )
770 .flatMap( List::stream )
771 .map( a -> a.isContainer( ) ? a.list( ) : Arrays.asList( a ) )
772 .flatMap( List::stream )
774 .map( this::getArtifactFromPath );
780 * Same as {@link #newArtifactStream(ContentItem)} but returns the collected stream as list.
782 * @param item the item the parent item
783 * @return the list of artifacts or a empty list of no artifacts where found
786 public List<? extends Artifact> getArtifacts( ContentItem item )
788 try(Stream<? extends Artifact> stream = newArtifactStream( item )) {
789 return stream.collect( Collectors.toList());
794 * Returns all artifacts
797 * @throws ContentAccessException
799 public Stream<? extends Artifact> newArtifactStream( Namespace item ) throws ContentAccessException
801 return newArtifactStream( ArchivaItemSelector.builder( ).withNamespace( item.getNamespace( ) ).build( ) );
804 public Stream<? extends Artifact> newArtifactStream( Project item ) throws ContentAccessException
806 return newArtifactStream( ArchivaItemSelector.builder( ).withNamespace( item.getNamespace( ).getNamespace() )
807 .withProjectId( item.getId() ).build( ) );
810 public Stream<? extends Artifact> newArtifactStream( Version item ) throws ContentAccessException
812 return newArtifactStream( ArchivaItemSelector.builder( ).withNamespace( item.getProject().getNamespace( ).getNamespace() )
813 .withProjectId( item.getProject().getId() )
814 .withVersion( item.getVersion() ).build( ) );
818 * Returns all related artifacts that match the given artifact. That means all artifacts that have
819 * the same filename plus an additional extension, e.g. ${fileName}.sha2
820 * @param item the artifact
821 * @return the stream of artifacts
822 * @throws ContentAccessException
824 public Stream<? extends Artifact> newArtifactStream( Artifact item ) throws ContentAccessException
826 final Version v = item.getVersion( );
827 final String fileName = item.getFileName( );
828 final Predicate<StorageAsset> filter = ( StorageAsset a ) ->
829 a.getName( ).startsWith( fileName + "." );
830 return v.getAsset( ).list( ).stream( ).filter( filter )
831 .map( a -> getArtifactFromPath( a ) );
834 * Returns the stream of artifacts that are children of the given item.
836 * @param item the item from where the artifacts should be returned
838 * @throws ContentAccessException
841 public Stream<? extends Artifact> newArtifactStream( ContentItem item ) throws ContentAccessException
843 if (item instanceof Namespace) {
844 return newArtifactStream( ( (Namespace) item ) );
845 } else if (item instanceof Project) {
846 return newArtifactStream( (Project) item );
847 } else if (item instanceof Version) {
848 return newArtifactStream( (Version) item );
849 } else if (item instanceof Artifact) {
850 return newArtifactStream( (Artifact) item );
853 log.warn( "newArtifactStream for unsupported item requested: {}", item.getClass( ).getName( ) );
854 return Stream.empty( );
859 * Checks, if the asset/file queried by the given selector exists.
862 public boolean hasContent( ItemSelector selector )
864 return getItem( selector ).getAsset( ).exists( );
868 * Moves the file to the artifact destination
871 public void addArtifact( Path sourceFile, Artifact destination ) throws IllegalArgumentException, ContentAccessException
875 StorageAsset asset = destination.getAsset( );
876 if (!asset.exists()) {
879 asset.replaceDataFromFile( sourceFile );
881 catch ( IOException e )
883 log.error( "Could not push data to asset source={} destination={}. {}", sourceFile, destination.getAsset().getFilePath(), e.getMessage( ) );
884 throw new ContentAccessException( e.getMessage( ), e );
889 public ContentItem toItem( String path ) throws LayoutException
891 StorageAsset asset = getRepository( ).getAsset( path );
894 ItemSelector selector = getPathParser( ).toItemSelector( path );
895 return getItem( selector );
897 return getItemFromPath( asset );
902 public ContentItem toItem( StorageAsset assetPath ) throws LayoutException
904 return toItem( assetPath.getPath( ) );
907 /// ************* End of new generation interface ******************
910 * Returns a version reference from the coordinates
911 * @param groupId the group id
912 * @param artifactId the artifact id
913 * @param version the version
914 * @return the versioned reference object
917 public VersionedReference toVersion( String groupId, String artifactId, String version ) {
918 return new VersionedReference().groupId( groupId ).artifactId( artifactId ).version( version );
922 public VersionedReference toGenericVersion( ArtifactReference artifactReference )
924 return toVersion( artifactReference.getGroupId( ), artifactReference.getArtifactId( ), VersionUtil.getBaseVersion( artifactReference.getVersion( ) ));
928 * Return the version the artifact is part of
929 * @param artifactReference
932 public VersionedReference toVersion( ArtifactReference artifactReference) {
933 return toVersion( artifactReference.getGroupId( ), artifactReference.getArtifactId( ), artifactReference.getVersion( ) );
937 public ArtifactReference toArtifact( String groupId, String artifactId, String version, String type, String classifier) {
938 return new ArtifactReference( ).groupId( groupId ).artifactId( artifactId ).version( version ).type( type ).classifier( classifier );
943 public void deleteVersion( VersionedReference ref ) throws ContentNotFoundException, ContentAccessException
945 final String path = toPath( ref );
946 final Path deleteTarget = getRepoDir().resolve(path);
947 if ( !Files.exists(deleteTarget) )
949 log.warn( "Version path for repository {} does not exist: {}", getId(), deleteTarget );
950 throw new ContentNotFoundException( "Version not found for repository "+getId()+": "+path );
952 if ( Files.isDirectory(deleteTarget) )
956 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
958 catch ( IOException e )
960 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
961 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
964 log.warn( "Version path for repository {} is not a directory {}", getId(), deleteTarget );
965 throw new ContentNotFoundException( "Version path for repository "+getId()+" is not directory: " + path );
970 public void deleteProject( ProjectReference ref )
971 throws ContentNotFoundException, ContentAccessException
973 final String path = toPath( ref );
974 final Path deleteTarget = getRepoDir( ).resolve( path );
975 if ( !Files.exists(deleteTarget) )
977 log.warn( "Project path for repository {} does not exist: {}", getId(), deleteTarget );
978 throw new ContentNotFoundException( "Project not found for repository "+getId()+": "+path );
980 if ( Files.isDirectory(deleteTarget) )
984 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
986 catch ( IOException e )
988 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
989 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
994 log.warn( "Project path for repository {} is not a directory {}", getId(), deleteTarget );
995 throw new ContentNotFoundException( "Project path for repository "+getId()+" is not directory: " + path );
1001 public void deleteProject( String namespace, String projectId ) throws ContentNotFoundException, ContentAccessException
1003 this.deleteProject( new ProjectReference().groupId( namespace ).artifactId( projectId ) );
1007 public void deleteArtifact( ArtifactReference ref ) throws ContentNotFoundException, ContentAccessException
1009 final String path = toPath( ref );
1010 final Path repoDir = getRepoDir( );
1011 Path deleteTarget = repoDir.resolve( path );
1012 if ( Files.exists(deleteTarget) )
1016 if (Files.isDirectory( deleteTarget ))
1018 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
1020 Files.delete( deleteTarget );
1023 catch ( IOException e )
1025 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
1026 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
1029 log.warn( "Artifact path for repository {} does not exist: {}", getId(), deleteTarget );
1030 throw new ContentNotFoundException( "Artifact not found for repository "+getId()+": "+path );
1036 public void deleteGroupId( String groupId )
1037 throws ContentNotFoundException, ContentAccessException
1039 final String path = toPath( groupId );
1040 final Path deleteTarget = getRepoDir( ).resolve( path );
1041 if (!Files.exists(deleteTarget)) {
1042 log.warn( "Namespace path for repository {} does not exist: {}", getId(), deleteTarget );
1043 throw new ContentNotFoundException( "Namespace not found for repository "+getId()+": "+path );
1045 if ( Files.isDirectory(deleteTarget) )
1049 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
1051 catch ( IOException e )
1053 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
1054 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
1057 log.warn( "Namespace path for repository {} is not a directory {}", getId(), deleteTarget );
1058 throw new ContentNotFoundException( "Namespace path for repository "+getId()+" is not directory: " + path );
1064 public String getId()
1066 return repository.getId();
1070 public List<ArtifactReference> getRelatedArtifacts( VersionedReference reference )
1071 throws ContentNotFoundException, LayoutException, ContentAccessException
1073 StorageAsset artifactDir = toFile( reference );
1074 if ( !artifactDir.exists())
1076 throw new ContentNotFoundException(
1077 "Unable to get related artifacts using a non-existant directory: " + artifactDir.getPath() );
1080 if ( !artifactDir.isContainer() )
1082 throw new ContentNotFoundException(
1083 "Unable to get related artifacts using a non-directory: " + artifactDir.getPath() );
1086 // First gather up the versions found as artifacts in the managed repository.
1088 try (Stream<? extends StorageAsset> stream = artifactDir.list().stream() ) {
1089 return stream.filter(asset -> !asset.isContainer()).map(path -> {
1091 ArtifactReference artifact = toArtifactReference(path.getPath());
1092 if( artifact.getGroupId().equals( reference.getGroupId() ) && artifact.getArtifactId().equals(
1093 reference.getArtifactId() ) && artifact.getVersion().equals( reference.getVersion() )) {
1098 } catch (LayoutException e) {
1099 log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
1102 }).filter(Objects::nonNull).collect(Collectors.toList());
1103 } catch (RuntimeException e) {
1104 Throwable cause = e.getCause( );
1106 if (cause instanceof LayoutException) {
1107 throw (LayoutException)cause;
1110 throw new ContentAccessException( cause.getMessage( ), cause );
1113 throw new ContentAccessException( e.getMessage( ), e );
1119 * Create the filter for various combinations of classifier and type
1121 private Predicate<ArtifactReference> getChecker(ArtifactReference referenceObject, String extension) {
1122 // TODO: Check, if extension is the correct parameter here
1123 // We compare type with extension which works for artifacts like .jar.md5 but may
1124 // be not the best way.
1126 if (referenceObject.getClassifier()!=null && referenceObject.getType()!=null) {
1127 return ((ArtifactReference a) ->
1128 referenceObject.getGroupId().equals( a.getGroupId() )
1129 && referenceObject.getArtifactId().equals( a.getArtifactId() )
1130 && referenceObject.getVersion( ).equals( a.getVersion( ) )
1131 && ( (a.getType()==null)
1132 || referenceObject.getType().equals( a.getType() )
1133 || a.getType().startsWith(extension) )
1134 && referenceObject.getClassifier().equals( a.getClassifier() )
1136 } else if (referenceObject.getClassifier()!=null && referenceObject.getType()==null){
1137 return ((ArtifactReference a) ->
1138 referenceObject.getGroupId().equals( a.getGroupId() )
1139 && referenceObject.getArtifactId().equals( a.getArtifactId() )
1140 && referenceObject.getVersion( ).equals( a.getVersion( ) )
1141 && referenceObject.getClassifier().equals( a.getClassifier() )
1143 } else if (referenceObject.getClassifier()==null && referenceObject.getType()!=null){
1144 return ((ArtifactReference a) ->
1145 referenceObject.getGroupId().equals( a.getGroupId() )
1146 && referenceObject.getArtifactId().equals( a.getArtifactId() )
1147 && referenceObject.getVersion( ).equals( a.getVersion( ) )
1148 && ( (a.getType()==null)
1149 || referenceObject.getType().equals( a.getType() )
1150 || a.getType().startsWith(extension) )
1153 return ((ArtifactReference a) ->
1154 referenceObject.getGroupId().equals( a.getGroupId() )
1155 && referenceObject.getArtifactId().equals( a.getArtifactId() )
1156 && referenceObject.getVersion( ).equals( a.getVersion( ) )
1164 public List<ArtifactReference> getRelatedArtifacts( ArtifactReference reference )
1165 throws ContentNotFoundException, LayoutException, ContentAccessException
1167 if ( StringUtils.isEmpty( reference.getType() ) && StringUtils.isEmpty( reference.getClassifier() ) ) {
1168 return getRelatedArtifacts( toVersion( reference ) );
1171 StorageAsset artifactFile = toFile( reference );
1172 StorageAsset repoDir = artifactFile.getParent();
1174 if (!artifactFile.isContainer()) {
1175 ext = StringUtils.substringAfterLast( artifactFile.getName(), ".");
1180 if ( !repoDir.exists())
1182 throw new ContentNotFoundException(
1183 "Unable to get related artifacts using a non-existant directory: " + repoDir.getPath() );
1186 if ( !repoDir.isContainer() )
1188 throw new ContentNotFoundException(
1189 "Unable to get related artifacts using a non-directory: " + repoDir.getPath() );
1192 // First gather up the versions found as artifacts in the managed repository.
1194 try (Stream<? extends StorageAsset> stream = repoDir.list().stream() ) {
1195 return stream.filter(
1196 asset -> !asset.isContainer())
1199 return toArtifactReference(path.getPath());
1200 } catch (LayoutException e) {
1201 log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
1204 }).filter(Objects::nonNull).filter(getChecker( reference, ext )).collect(Collectors.toList());
1205 } catch (RuntimeException e) {
1206 Throwable cause = e.getCause( );
1208 if (cause instanceof LayoutException) {
1209 throw (LayoutException)cause;
1212 throw new ContentAccessException( cause.getMessage( ), cause );
1215 throw new ContentAccessException( e.getMessage( ), e );
1221 public List<StorageAsset> getRelatedAssets( ArtifactReference reference ) throws ContentNotFoundException, LayoutException, ContentAccessException
1227 public String getRepoRoot()
1229 return convertUriToPath( repository.getLocation() );
1232 private String convertUriToPath( URI uri ) {
1233 if (uri.getScheme()==null) {
1234 return Paths.get(uri.getPath()).toString();
1235 } else if ("file".equals(uri.getScheme())) {
1236 return Paths.get(uri).toString();
1238 return uri.toString();
1243 public ManagedRepository getRepository()
1249 * Gather the Available Versions (on disk) for a specific Project Reference, based on filesystem
1252 * @return the Set of available versions, based on the project reference.
1253 * @throws LayoutException
1256 public Set<String> getVersions( ProjectReference reference )
1257 throws ContentNotFoundException, LayoutException, ContentAccessException
1259 final String path = toPath( reference );
1260 final Path projDir = getRepoDir().resolve(toPath(reference));
1261 if ( !Files.exists(projDir) )
1263 throw new ContentNotFoundException(
1264 "Unable to get Versions on a non-existant directory for repository "+getId()+": " + path );
1267 if ( !Files.isDirectory(projDir) )
1269 throw new ContentNotFoundException(
1270 "Unable to get Versions on a non-directory for repository "+getId()+": " + path );
1273 final String groupId = reference.getGroupId();
1274 final String artifactId = reference.getArtifactId();
1275 try(Stream<Path> stream = Files.list(projDir)) {
1276 return stream.filter(Files::isDirectory).map(
1277 p -> toVersion(groupId, artifactId, p.getFileName().toString())
1278 ).filter(this::hasArtifact).map(ref -> ref.getVersion())
1279 .collect(Collectors.toSet());
1280 } catch (IOException e) {
1281 log.error("Could not read directory {}: {}", projDir, e.getMessage(), e);
1282 throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, e );
1283 } catch (RuntimeException e) {
1284 Throwable cause = e.getCause( );
1287 if ( cause instanceof LayoutException )
1289 throw (LayoutException) cause;
1291 log.error("Could not read directory {}: {}", projDir, cause.getMessage(), cause);
1292 throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, cause );
1295 log.error("Could not read directory {}: {}", projDir, e.getMessage(), e);
1296 throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, cause );
1302 public Set<String> getVersions( VersionedReference reference )
1303 throws ContentNotFoundException, ContentAccessException, LayoutException
1305 try(Stream<ArtifactReference> stream = newArtifactStream( reference ))
1307 return stream.filter( Objects::nonNull )
1308 .map( ar -> ar.getVersion( ) )
1309 .collect( Collectors.toSet( ) );
1310 } catch (IOException e) {
1311 final String path = toPath( reference );
1312 log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
1313 throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, e );
1318 public boolean hasContent( ArtifactReference reference ) throws ContentAccessException
1320 StorageAsset artifactFile = toFile( reference );
1321 return artifactFile.exists() && !artifactFile.isContainer();
1325 public boolean hasContent( ProjectReference reference ) throws ContentAccessException
1329 Set<String> versions = getVersions( reference );
1330 return !versions.isEmpty();
1332 catch ( ContentNotFoundException | LayoutException e )
1339 public boolean hasContent( VersionedReference reference ) throws ContentAccessException
1343 return ( getFirstArtifact( reference ) != null );
1345 catch ( LayoutException | ContentNotFoundException e )
1349 catch ( IOException e )
1351 String path = toPath( reference );
1352 log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
1353 throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
1358 public void setRepository( final ManagedRepository repo )
1360 this.repository = repo;
1362 if (repository instanceof EditableManagedRepository) {
1363 ((EditableManagedRepository) repository).setContent(this);
1368 private Path getRepoDir() {
1369 return repository.getAsset( "" ).getFilePath( );
1372 private RepositoryStorage getStorage() {
1373 return repository.getAsset( "" ).getStorage( );
1377 * Convert a path to an artifact reference.
1379 * @param path the path to convert. (relative or full location path)
1380 * @throws LayoutException if the path cannot be converted to an artifact reference.
1383 public ArtifactReference toArtifactReference( String path )
1384 throws LayoutException
1386 String repoPath = convertUriToPath( repository.getLocation() );
1387 if ( ( path != null ) && path.startsWith( repoPath ) && repoPath.length() > 0 )
1389 return super.toArtifactReference( path.substring( repoPath.length() + 1 ) );
1392 if (repoPath!=null) {
1393 while (repoPath.startsWith("/")) {
1394 repoPath = repoPath.substring(1);
1397 return super.toArtifactReference( repoPath );
1402 // The variant with runtime exception for stream usage
1403 private ArtifactReference toArtifactRef(String path) {
1405 return toArtifactReference(path);
1406 } catch (LayoutException e) {
1407 throw new RuntimeException(e);
1414 public StorageAsset toFile( ArtifactReference reference )
1416 return repository.getAsset(toPath(reference));
1420 public StorageAsset toFile( ArchivaArtifact reference )
1422 return repository.getAsset( toPath( reference ) );
1426 public StorageAsset toFile( VersionedReference reference )
1428 return repository.getAsset( toPath( reference ) );
1432 * Get the first Artifact found in the provided VersionedReference location.
1434 * @param reference the reference to the versioned reference to search within
1435 * @return the ArtifactReference to the first artifact located within the versioned reference. or null if
1436 * no artifact was found within the versioned reference.
1437 * @throws java.io.IOException if the versioned reference is invalid (example: doesn't exist, or isn't a directory)
1438 * @throws LayoutException
1440 private ArtifactReference getFirstArtifact( VersionedReference reference )
1441 throws ContentNotFoundException, LayoutException, IOException
1443 try(Stream<ArtifactReference> stream = newArtifactStream( reference ))
1445 return stream.findFirst( ).orElse( null );
1446 } catch (RuntimeException e) {
1447 throw new ContentNotFoundException( e.getMessage( ), e.getCause( ) );
1451 private Stream<ArtifactReference> newArtifactStream( VersionedReference reference) throws ContentNotFoundException, LayoutException, IOException {
1452 final Path repoBase = getRepoDir( );
1453 String path = toMetadataPath( reference );
1454 Path versionDir = repoBase.resolve( path ).getParent();
1455 if ( !Files.exists(versionDir) )
1457 throw new ContentNotFoundException( "Unable to gather the list of artifacts on a non-existant directory: "
1458 + versionDir.toAbsolutePath() );
1461 if ( !Files.isDirectory(versionDir) )
1463 throw new ContentNotFoundException(
1464 "Unable to gather the list of snapshot versions on a non-directory: " + versionDir.toAbsolutePath() );
1466 return Files.list(versionDir).filter(Files::isRegularFile)
1467 .map(p -> repoBase.relativize(p).toString())
1468 .filter(p -> !filetypes.matchesDefaultExclusions(p))
1469 .filter(filetypes::matchesArtifactPattern)
1470 .map(this::toArtifactRef);
1473 public List<ArtifactReference> getArtifacts(VersionedReference reference) throws ContentNotFoundException, LayoutException, ContentAccessException
1475 try (Stream<ArtifactReference> stream = newArtifactStream( reference ))
1477 return stream.collect( Collectors.toList( ) );
1478 } catch ( IOException e )
1480 String path = toPath( reference );
1481 log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
1482 throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
1487 private boolean hasArtifact( VersionedReference reference )
1490 try(Stream<ArtifactReference> stream = newArtifactStream( reference ))
1492 return stream.anyMatch( e -> true );
1493 } catch (ContentNotFoundException e) {
1495 } catch ( LayoutException | IOException e) {
1496 // We throw the runtime exception for better stream handling
1497 throw new RuntimeException(e);
1501 public void setFiletypes( FileTypes filetypes )
1503 this.filetypes = filetypes;
1506 public void setMavenContentHelper( MavenContentHelper contentHelper) {
1507 this.mavenContentHelper = contentHelper;