1 package org.apache.archiva.repository.content.maven2;
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
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import org.apache.archiva.common.filelock.FileLockManager;
23 import org.apache.archiva.common.utils.FileUtils;
24 import org.apache.archiva.common.utils.VersionUtil;
25 import org.apache.archiva.configuration.FileTypes;
26 import org.apache.archiva.maven2.metadata.MavenMetadataReader;
27 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
28 import org.apache.archiva.metadata.repository.storage.maven2.ArtifactMappingProvider;
29 import org.apache.archiva.metadata.repository.storage.maven2.DefaultArtifactMappingProvider;
30 import org.apache.archiva.model.ArchivaArtifact;
31 import org.apache.archiva.model.ArtifactReference;
32 import org.apache.archiva.model.ProjectReference;
33 import org.apache.archiva.model.VersionedReference;
34 import org.apache.archiva.repository.ContentAccessException;
35 import org.apache.archiva.repository.ContentNotFoundException;
36 import org.apache.archiva.repository.EditableManagedRepository;
37 import org.apache.archiva.repository.LayoutException;
38 import org.apache.archiva.repository.ManagedRepository;
39 import org.apache.archiva.repository.ManagedRepositoryContent;
40 import org.apache.archiva.repository.content.Artifact;
41 import org.apache.archiva.repository.content.ContentItem;
42 import org.apache.archiva.repository.content.ItemNotFoundException;
43 import org.apache.archiva.repository.content.ItemSelector;
44 import org.apache.archiva.repository.content.Namespace;
45 import org.apache.archiva.repository.content.Project;
46 import org.apache.archiva.repository.content.Version;
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.storage.RepositoryStorage;
52 import org.apache.archiva.repository.storage.StorageAsset;
53 import org.apache.archiva.repository.storage.util.StorageUtil;
54 import org.apache.commons.collections4.map.ReferenceMap;
55 import org.apache.commons.lang3.StringUtils;
57 import javax.inject.Inject;
58 import javax.inject.Named;
59 import java.io.IOException;
61 import java.nio.file.Files;
62 import java.nio.file.Path;
63 import java.nio.file.Paths;
64 import java.util.Collections;
65 import java.util.List;
66 import java.util.Objects;
67 import java.util.Optional;
69 import java.util.function.Predicate;
70 import java.util.regex.Matcher;
71 import java.util.regex.Pattern;
72 import java.util.stream.Collectors;
73 import java.util.stream.Stream;
76 * ManagedDefaultRepositoryContent
78 public class ManagedDefaultRepositoryContent
79 extends AbstractDefaultRepositoryContent
80 implements ManagedRepositoryContent
83 public static final String METADATA_FILENAME = "maven-metadata.xml";
84 private FileTypes filetypes;
86 public void setFileTypes(FileTypes fileTypes) {
87 this.filetypes = fileTypes;
90 private ManagedRepository repository;
92 private FileLockManager lockManager;
95 @Named("repositoryPathTranslator#maven2")
96 private RepositoryPathTranslator pathTranslator;
99 @Named( "metadataReader#maven" )
100 MavenMetadataReader metadataReader;
103 @Named( "MavenContentHelper" )
104 MavenContentHelper mavenContentHelper;
106 public static final String SNAPSHOT = "SNAPSHOT";
108 public static final Pattern UNIQUE_SNAPSHOT_PATTERN = Pattern.compile( "^(SNAPSHOT|[0-9]{8}\\.[0-9]{6}-[0-9]+)(.*)" );
109 public static final Pattern CLASSIFIER_PATTERN = Pattern.compile( "^-([^.]+)(\\..*)" );
111 public static final Pattern TIMESTAMP_PATTERN = Pattern.compile( "^([0-9]{8})\\.([0-9]{6})$" );
113 public static final Pattern GENERIC_SNAPSHOT_PATTERN = Pattern.compile( "^(.*)-" + SNAPSHOT );
116 * We are caching content items in a weak reference map. To avoid always recreating the
117 * the hierarchical structure.
118 * TODO: Better use a object cache? E.g. our spring cache implementation?
120 private ReferenceMap<String, Namespace> namespaceMap = new ReferenceMap<>( );
121 private ReferenceMap<StorageAsset, Project> projectMap = new ReferenceMap<>( );
122 private ReferenceMap<StorageAsset, Version> versionMap = new ReferenceMap<>( );
123 private ReferenceMap<StorageAsset, Artifact> artifactMap = new ReferenceMap<>( );
125 public ManagedDefaultRepositoryContent() {
126 super(Collections.singletonList( new DefaultArtifactMappingProvider() ));
129 public ManagedDefaultRepositoryContent(ManagedRepository repository, FileTypes fileTypes, FileLockManager lockManager) {
130 super(Collections.singletonList( new DefaultArtifactMappingProvider() ));
131 setFileTypes( fileTypes );
132 this.lockManager = lockManager;
133 setRepository( repository );
136 public ManagedDefaultRepositoryContent( ManagedRepository repository, List<? extends ArtifactMappingProvider> artifactMappingProviders, FileTypes fileTypes, FileLockManager lockManager )
138 super(artifactMappingProviders==null ? Collections.singletonList( new DefaultArtifactMappingProvider() ) : artifactMappingProviders);
139 setFileTypes( fileTypes );
140 this.lockManager = lockManager;
141 setRepository( repository );
146 * Returns a version reference from the coordinates
147 * @param groupId the group id
148 * @param artifactId the artifact id
149 * @param version the version
150 * @return the versioned reference object
153 public VersionedReference toVersion( String groupId, String artifactId, String version ) {
154 return new VersionedReference().groupId( groupId ).artifactId( artifactId ).version( version );
158 public VersionedReference toGenericVersion( ArtifactReference artifactReference )
160 return toVersion( artifactReference.getGroupId( ), artifactReference.getArtifactId( ), VersionUtil.getBaseVersion( artifactReference.getVersion( ) ));
164 * Return the version the artifact is part of
165 * @param artifactReference
168 public VersionedReference toVersion( ArtifactReference artifactReference) {
169 return toVersion( artifactReference.getGroupId( ), artifactReference.getArtifactId( ), artifactReference.getVersion( ) );
173 public ArtifactReference toArtifact( String groupId, String artifactId, String version, String type, String classifier) {
174 return new ArtifactReference( ).groupId( groupId ).artifactId( artifactId ).version( version ).type( type ).classifier( classifier );
178 public void deleteItem( ContentItem item ) throws ItemNotFoundException, ContentAccessException
180 final Path baseDirectory = getRepoDir( );
181 final Path itemPath = item.getAsset( ).getFilePath( );
182 if ( !Files.exists( itemPath ) )
184 throw new ItemNotFoundException( "The item " + item.toString() + "does not exist in the repository " + getId( ) );
186 if ( !itemPath.toAbsolutePath().startsWith( baseDirectory.toAbsolutePath() ) )
188 log.error( "The namespace {} to delete from repository {} is not a subdirectory of the repository base.", item, getId( ) );
189 log.error( "Namespace directory: {}", itemPath );
190 log.error( "Repository directory: {}", baseDirectory );
191 throw new ContentAccessException( "Inconsistent directories found. Could not delete namespace." );
195 if (Files.isDirectory( itemPath ))
197 FileUtils.deleteDirectory( itemPath );
199 Files.deleteIfExists( itemPath );
202 catch ( IOException e )
204 log.error( "Could not delete namespace directory {}: {}", itemPath, e.getMessage( ), e );
205 throw new ContentAccessException( "Error occured while deleting namespace " + item + ": " + e.getMessage( ), e );
209 private StorageAsset getAssetByPath(String assetPath) {
210 return getStorage( ).getAsset( assetPath );
213 private StorageAsset getAsset(String namespace) {
214 String namespacePath = formatAsDirectory( namespace.trim() );
215 if (StringUtils.isEmpty( namespacePath )) {
218 return getAssetByPath(namespacePath);
221 private StorageAsset getAsset(String namespace, String project) {
222 return getAsset( namespace ).resolve( project );
225 private StorageAsset getAsset(String namespace, String project, String version) {
226 return getAsset( namespace, project ).resolve( version );
229 private StorageAsset getAsset(String namespace, String project, String version, String fileName) {
230 return getAsset( namespace, project, version ).resolve( fileName );
235 public Namespace getNamespace( final ItemSelector namespaceSelector ) throws ContentAccessException, IllegalArgumentException
237 return namespaceMap.computeIfAbsent( namespaceSelector.getNamespace(),
239 StorageAsset nsPath = getAsset( namespace );
240 return ArchivaNamespace.withRepository( this ).withAsset( nsPath ).
241 withNamespace( namespace ).build( );
247 public Project getProject( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
249 if (!selector.hasProjectId()) {
250 throw new IllegalArgumentException( "Project id must be set" );
252 final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ) );
253 return projectMap.computeIfAbsent( path, projectPath -> {
254 final Namespace ns = getNamespace( selector );
255 return ArchivaProject.withAsset( projectPath ).withNamespace( ns ).withId( selector.getProjectId( ) ).build( );
262 public Version getVersion( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
264 if (!selector.hasProjectId()) {
265 throw new IllegalArgumentException( "Project id must be set" );
267 if (!selector.hasVersion() ) {
268 throw new IllegalArgumentException( "Version must be set" );
270 final StorageAsset path = getAsset(selector.getNamespace(), selector.getProjectId(), selector.getVersion());
271 return versionMap.computeIfAbsent( path, versionPath -> {
272 final Project project = getProject( selector );
273 return ArchivaVersion.withAsset( path )
274 .withProject( project )
275 .withVersion( selector.getVersion( ) ).build();
281 public Artifact createArtifact(final StorageAsset artifactPath, final ItemSelector selector,
282 final String classifier, final String extension) {
283 Version version = getVersion(selector);
284 ArtifactOptBuilder builder = org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
285 .withVersion( version )
286 .withId( selector.getArtifactId( ) )
287 .withArtifactVersion( mavenContentHelper.getArtifactVersion( artifactPath, selector ) )
288 .withClassifier( classifier );
289 if (selector.hasType()) {
290 builder.withType( selector.getType( ) );
292 return builder.build( );
295 public Namespace getNamespaceFromArtifactPath( final StorageAsset artifactPath) {
296 final StorageAsset namespacePath = artifactPath.getParent( ).getParent( ).getParent( );
297 final String namespace = MavenContentHelper.getNamespaceFromNamespacePath( namespacePath );
298 return namespaceMap.computeIfAbsent( namespace,
299 myNamespace -> ArchivaNamespace.withRepository( this )
300 .withAsset( namespacePath )
301 .withNamespace( namespace )
305 private Project getProjectFromArtifactPath( final StorageAsset artifactPath) {
306 final StorageAsset projectPath = artifactPath.getParent( ).getParent( );
307 return projectMap.computeIfAbsent( projectPath,
308 myProjectPath -> ArchivaProject.withAsset( projectPath )
309 .withNamespace( getNamespaceFromArtifactPath( artifactPath ) )
310 .withId( projectPath.getName( ) ).build( )
314 private Version getVersionFromArtifactPath( final StorageAsset artifactPath) {
315 final StorageAsset versionPath = artifactPath.getParent( );
316 return versionMap.computeIfAbsent( versionPath,
317 myVersionPath -> ArchivaVersion.withAsset( versionPath )
318 .withProject( getProjectFromArtifactPath( artifactPath ) )
319 .withVersion( versionPath.getName( ) ).build( ) );
322 private Artifact getArtifactFromPath(final StorageAsset artifactPath) {
323 final Version version = getVersionFromArtifactPath( artifactPath );
324 final ArtifactInfo info = getArtifactInfoFromPath( version.getVersion(), artifactPath );
325 return artifactMap.computeIfAbsent( artifactPath, myArtifactPath ->
326 org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
327 .withVersion( version )
329 .withClassifier( info.classifier )
330 .withRemainder( info.remainder )
331 .withType( info.type )
332 .withArtifactVersion( info.version )
333 .withContentType( info.contentType )
338 private ContentItem getItemFromPath(final StorageAsset itemPath) {
339 if (itemPath.isLeaf()) {
340 return getArtifactFromPath( itemPath );
342 if (versionMap.containsKey( itemPath )) {
343 return versionMap.get( itemPath );
345 if (projectMap.containsKey( itemPath )) {
346 return projectMap.get( itemPath );
348 String ns = MavenContentHelper.getNamespaceFromNamespacePath( itemPath );
349 if (namespaceMap.containsKey( ns )) {
350 return namespaceMap.get( ns );
352 // No cached item, so we have to gather more information:
353 // Check for version directory (contains at least a pom or metadata file)
354 if (itemPath.list( ).stream( ).map(a -> a.getName().toLowerCase()).anyMatch( n ->
356 || n.startsWith( "maven-metadata" )
358 return versionMap.computeIfAbsent( itemPath,
359 myVersionPath -> ArchivaVersion.withAsset( itemPath )
360 .withProject( (Project)getItemFromPath( itemPath.getParent() ) )
361 .withVersion( itemPath.getName() ).build());
363 // We have to dig further and find the next directory with a pom
364 Optional<StorageAsset> foundFile = StorageUtil.newAssetStream( itemPath )
365 .filter( a -> a.getName().toLowerCase().endsWith( ".pom" )
366 || a.getName().toLowerCase().startsWith( "maven-metadata" ) )
368 if (foundFile.isPresent())
371 StorageAsset current = foundFile.get( );
372 while (current.hasParent() && !current.equals(itemPath)) {
374 current = current.getParent( );
376 // Project path if it is one level up from the found file
378 return projectMap.computeIfAbsent( itemPath,
379 myItemPath -> getProjectFromArtifactPath( foundFile.get( ) ) );
381 // All other paths are treated as namespace
382 return namespaceMap.computeIfAbsent( ns,
383 myNamespace -> ArchivaNamespace.withRepository( this )
384 .withAsset( itemPath )
389 // Don't know what to do with it, so we treat it as namespace path
390 return namespaceMap.computeIfAbsent( ns,
391 myNamespace -> ArchivaNamespace.withRepository( this )
392 .withAsset( itemPath )
401 // Simple object to hold artifact information
402 private class ArtifactInfo {
404 private String version;
405 private String extension;
406 private String remainder;
408 private String classifier;
409 private String contentType;
412 private ArtifactInfo getArtifactInfoFromPath(String genericVersion, StorageAsset path) {
413 final ArtifactInfo info = new ArtifactInfo( );
414 info.id = path.getParent( ).getParent( ).getName( );
415 final String fileName = path.getName( );
416 if ( genericVersion.endsWith( "-" + SNAPSHOT ) )
418 String baseVersion = StringUtils.substringBeforeLast( genericVersion, "-" + SNAPSHOT );
419 String prefix = info.id+"-"+baseVersion+"-";
420 if (fileName.startsWith( prefix ))
422 String versionPostfix = StringUtils.removeStart( fileName, prefix );
423 Matcher matcher = UNIQUE_SNAPSHOT_PATTERN.matcher( versionPostfix );
424 if (matcher.matches()) {
425 info.version = baseVersion + "-" + matcher.group( 1 );
426 String newPrefix = prefix + info.version;
427 if (fileName.startsWith( newPrefix ))
429 String classPostfix = StringUtils.removeStart( fileName, newPrefix );
430 Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
431 if (cMatch.matches()) {
432 info.classifier = cMatch.group( 1 );
433 info.remainder = cMatch.group( 2 );
435 info.classifier = "";
436 info.remainder = classPostfix;
439 log.error( "Artifact does not match the maven name pattern {}", path );
440 info.classifier = "";
441 info.remainder = StringUtils.substringAfter( fileName, prefix );
444 log.error( "Artifact does not match the snapshot version pattern {}", path );
446 info.classifier = "";
447 info.remainder = StringUtils.substringAfter( fileName, prefix );
450 log.error( "Artifact does not match the maven name pattern: {}", path );
452 info.classifier = "";
453 info.remainder = StringUtils.substringAfterLast( fileName, "." );
456 String prefix = info.id+"-"+genericVersion;
457 if (fileName.startsWith( prefix ))
459 info.version=genericVersion;
460 String classPostfix = StringUtils.removeStart( fileName, prefix );
461 Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
462 if (cMatch.matches()) {
463 info.classifier = cMatch.group( 1 );
464 info.remainder = cMatch.group( 2 );
466 info.classifier = "";
467 info.remainder = classPostfix;
470 log.error( "Artifact does not match the version pattern {}", path );
472 info.classifier = "";
473 info.remainder = StringUtils.substringAfterLast( fileName, "." );
476 info.extension = StringUtils.substringAfterLast( fileName, "." );
477 info.type = MavenContentHelper.getTypeFromClassifierAndExtension( info.classifier, info.extension );
479 info.contentType = Files.probeContentType( path.getFilePath( ) );
480 } catch (IOException e) {
481 info.contentType = "";
489 public Artifact getArtifact( final ItemSelector selector ) throws ContentAccessException
491 if (!selector.hasProjectId( )) {
492 throw new IllegalArgumentException( "Project id must be set" );
494 if (!selector.hasVersion( )) {
495 throw new IllegalArgumentException( "Version must be set" );
497 if (!selector.hasArtifactId( )) {
498 throw new IllegalArgumentException( "Artifact Id must be set" );
500 final StorageAsset artifactDir = getAsset(selector.getNamespace(), selector.getProjectId(),
501 selector.getVersion());
502 final String artifactVersion = mavenContentHelper.getArtifactVersion( artifactDir, selector );
503 final String classifier = MavenContentHelper.getClassifier( selector );
504 final String extension = MavenContentHelper.getArtifactExtension( selector );
505 final String fileName = MavenContentHelper.getArtifactFileName( selector, artifactVersion, classifier, extension );
506 final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ),
507 selector.getVersion( ), fileName );
508 return artifactMap.computeIfAbsent( path, artifactPath -> createArtifact( path, selector, classifier, extension ) );
511 private StorageAsset getBasePathFromSelector(ItemSelector selector) {
512 StringBuilder path = new StringBuilder( );
513 if (selector.hasNamespace()) {
514 path.append(String.join( "/", getNamespace( selector ).getNamespacePath( ) ));
516 if (selector.hasProjectId()) {
517 path.append( "/" ).append( selector.getProjectId( ) );
519 if (selector.hasVersion()) {
520 path.append( "/" ).append( selector.getVersion( ) );
522 return getStorage( ).getAsset( path.toString( ) );
526 * File filter to select certain artifacts using the selector data.
528 private Predicate<StorageAsset> getFileFilterFromSelector(final ItemSelector selector) {
529 Predicate<StorageAsset> p = a -> a.isLeaf( );
530 if (selector.hasArtifactId()) {
531 final String pattern = selector.getArtifactId( );
532 p = p.and( a -> StringUtils.startsWithIgnoreCase( a.getName( ), pattern ) );
534 if (selector.hasArtifactVersion()) {
535 final String pattern = selector.getArtifactVersion( );
536 p = p.and( a -> StringUtils.containsIgnoreCase( a.getName( ), pattern ) );
538 if (selector.hasExtension()) {
539 final String pattern = "."+selector.getExtension( );
540 p = p.and( a -> StringUtils.endsWithIgnoreCase( a.getName( ), pattern ) );
541 } else if (selector.hasType()) {
542 final String pattern = "."+ MavenContentHelper.getArtifactExtension( selector );
543 p = p.and( a -> StringUtils.endsWithIgnoreCase( a.getName( ), pattern ) );
545 if (selector.hasClassifier()) {
546 final String pattern = "-" + selector.getClassifier( ) + ".";
547 p = p.and( a -> StringUtils.containsIgnoreCase( a.getName( ), pattern ) );
548 } else if (selector.hasType()) {
549 final String pattern = "-" + MavenContentHelper.getClassifierFromType( selector.getType( ) ) + ".";
550 p = p.and( a -> StringUtils.containsIgnoreCase( a.getName( ).toLowerCase( ), pattern ) );
559 public List<? extends Artifact> getAllArtifacts( ItemSelector selector ) throws ContentAccessException
568 public Stream<? extends Artifact> getAllArtifactStream( ItemSelector selector ) throws ContentAccessException
577 public List<? extends Project> getProjects( Namespace namespace )
586 public List<? extends Version> getVersions( Project project )
595 public List<? extends Artifact> getArtifacts( ContentItem item )
604 public List<? extends Artifact> getArtifactsStartingWith( Namespace namespace )
613 public Stream<? extends Artifact> getArtifactStream( ContentItem item )
622 public Stream<? extends Artifact> getArtifactStreamStartingWith( Namespace namespace )
631 public boolean hasContent( ItemSelector selector )
640 public void copyArtifact( Path sourceFile, ItemSelector destination ) throws IllegalArgumentException
646 public void deleteVersion( VersionedReference ref ) throws ContentNotFoundException, ContentAccessException
648 final String path = toPath( ref );
649 final Path deleteTarget = getRepoDir().resolve(path);
650 if ( !Files.exists(deleteTarget) )
652 log.warn( "Version path for repository {} does not exist: {}", getId(), deleteTarget );
653 throw new ContentNotFoundException( "Version not found for repository "+getId()+": "+path );
655 if ( Files.isDirectory(deleteTarget) )
659 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
661 catch ( IOException e )
663 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
664 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
667 log.warn( "Version path for repository {} is not a directory {}", getId(), deleteTarget );
668 throw new ContentNotFoundException( "Version path for repository "+getId()+" is not directory: " + path );
673 public void deleteProject( ProjectReference ref )
674 throws ContentNotFoundException, ContentAccessException
676 final String path = toPath( ref );
677 final Path deleteTarget = getRepoDir( ).resolve( path );
678 if ( !Files.exists(deleteTarget) )
680 log.warn( "Project path for repository {} does not exist: {}", getId(), deleteTarget );
681 throw new ContentNotFoundException( "Project not found for repository "+getId()+": "+path );
683 if ( Files.isDirectory(deleteTarget) )
687 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
689 catch ( IOException e )
691 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
692 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
697 log.warn( "Project path for repository {} is not a directory {}", getId(), deleteTarget );
698 throw new ContentNotFoundException( "Project path for repository "+getId()+" is not directory: " + path );
704 public void deleteProject( String namespace, String projectId ) throws ContentNotFoundException, ContentAccessException
706 this.deleteProject( new ProjectReference().groupId( namespace ).artifactId( projectId ) );
710 public void deleteArtifact( ArtifactReference ref ) throws ContentNotFoundException, ContentAccessException
712 final String path = toPath( ref );
713 final Path repoDir = getRepoDir( );
714 Path deleteTarget = repoDir.resolve( path );
715 if ( Files.exists(deleteTarget) )
719 if (Files.isDirectory( deleteTarget ))
721 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
723 Files.delete( deleteTarget );
726 catch ( IOException e )
728 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
729 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
732 log.warn( "Artifact path for repository {} does not exist: {}", getId(), deleteTarget );
733 throw new ContentNotFoundException( "Artifact not found for repository "+getId()+": "+path );
739 public void deleteGroupId( String groupId )
740 throws ContentNotFoundException, ContentAccessException
742 final String path = toPath( groupId );
743 final Path deleteTarget = getRepoDir( ).resolve( path );
744 if (!Files.exists(deleteTarget)) {
745 log.warn( "Namespace path for repository {} does not exist: {}", getId(), deleteTarget );
746 throw new ContentNotFoundException( "Namespace not found for repository "+getId()+": "+path );
748 if ( Files.isDirectory(deleteTarget) )
752 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
754 catch ( IOException e )
756 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
757 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
760 log.warn( "Namespace path for repository {} is not a directory {}", getId(), deleteTarget );
761 throw new ContentNotFoundException( "Namespace path for repository "+getId()+" is not directory: " + path );
767 public String getId()
769 return repository.getId();
773 public List<ArtifactReference> getRelatedArtifacts( VersionedReference reference )
774 throws ContentNotFoundException, LayoutException, ContentAccessException
776 StorageAsset artifactDir = toFile( reference );
777 if ( !artifactDir.exists())
779 throw new ContentNotFoundException(
780 "Unable to get related artifacts using a non-existant directory: " + artifactDir.getPath() );
783 if ( !artifactDir.isContainer() )
785 throw new ContentNotFoundException(
786 "Unable to get related artifacts using a non-directory: " + artifactDir.getPath() );
789 // First gather up the versions found as artifacts in the managed repository.
791 try (Stream<? extends StorageAsset> stream = artifactDir.list().stream() ) {
792 return stream.filter(asset -> !asset.isContainer()).map(path -> {
794 ArtifactReference artifact = toArtifactReference(path.getPath());
795 if( artifact.getGroupId().equals( reference.getGroupId() ) && artifact.getArtifactId().equals(
796 reference.getArtifactId() ) && artifact.getVersion().equals( reference.getVersion() )) {
801 } catch (LayoutException e) {
802 log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
805 }).filter(Objects::nonNull).collect(Collectors.toList());
806 } catch (RuntimeException e) {
807 Throwable cause = e.getCause( );
809 if (cause instanceof LayoutException) {
810 throw (LayoutException)cause;
813 throw new ContentAccessException( cause.getMessage( ), cause );
816 throw new ContentAccessException( e.getMessage( ), e );
822 * Create the filter for various combinations of classifier and type
824 private Predicate<ArtifactReference> getChecker(ArtifactReference referenceObject, String extension) {
825 // TODO: Check, if extension is the correct parameter here
826 // We compare type with extension which works for artifacts like .jar.md5 but may
827 // be not the best way.
829 if (referenceObject.getClassifier()!=null && referenceObject.getType()!=null) {
830 return ((ArtifactReference a) ->
831 referenceObject.getGroupId().equals( a.getGroupId() )
832 && referenceObject.getArtifactId().equals( a.getArtifactId() )
833 && referenceObject.getVersion( ).equals( a.getVersion( ) )
834 && ( (a.getType()==null)
835 || referenceObject.getType().equals( a.getType() )
836 || a.getType().startsWith(extension) )
837 && referenceObject.getClassifier().equals( a.getClassifier() )
839 } else if (referenceObject.getClassifier()!=null && referenceObject.getType()==null){
840 return ((ArtifactReference a) ->
841 referenceObject.getGroupId().equals( a.getGroupId() )
842 && referenceObject.getArtifactId().equals( a.getArtifactId() )
843 && referenceObject.getVersion( ).equals( a.getVersion( ) )
844 && referenceObject.getClassifier().equals( a.getClassifier() )
846 } else if (referenceObject.getClassifier()==null && referenceObject.getType()!=null){
847 return ((ArtifactReference a) ->
848 referenceObject.getGroupId().equals( a.getGroupId() )
849 && referenceObject.getArtifactId().equals( a.getArtifactId() )
850 && referenceObject.getVersion( ).equals( a.getVersion( ) )
851 && ( (a.getType()==null)
852 || referenceObject.getType().equals( a.getType() )
853 || a.getType().startsWith(extension) )
856 return ((ArtifactReference a) ->
857 referenceObject.getGroupId().equals( a.getGroupId() )
858 && referenceObject.getArtifactId().equals( a.getArtifactId() )
859 && referenceObject.getVersion( ).equals( a.getVersion( ) )
867 public List<ArtifactReference> getRelatedArtifacts( ArtifactReference reference )
868 throws ContentNotFoundException, LayoutException, ContentAccessException
870 if ( StringUtils.isEmpty( reference.getType() ) && StringUtils.isEmpty( reference.getClassifier() ) ) {
871 return getRelatedArtifacts( toVersion( reference ) );
874 StorageAsset artifactFile = toFile( reference );
875 StorageAsset repoDir = artifactFile.getParent();
877 if (!artifactFile.isContainer()) {
878 ext = StringUtils.substringAfterLast( artifactFile.getName(), ".");
883 if ( !repoDir.exists())
885 throw new ContentNotFoundException(
886 "Unable to get related artifacts using a non-existant directory: " + repoDir.getPath() );
889 if ( !repoDir.isContainer() )
891 throw new ContentNotFoundException(
892 "Unable to get related artifacts using a non-directory: " + repoDir.getPath() );
895 // First gather up the versions found as artifacts in the managed repository.
897 try (Stream<? extends StorageAsset> stream = repoDir.list().stream() ) {
898 return stream.filter(
899 asset -> !asset.isContainer())
902 return toArtifactReference(path.getPath());
903 } catch (LayoutException e) {
904 log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
907 }).filter(Objects::nonNull).filter(getChecker( reference, ext )).collect(Collectors.toList());
908 } catch (RuntimeException e) {
909 Throwable cause = e.getCause( );
911 if (cause instanceof LayoutException) {
912 throw (LayoutException)cause;
915 throw new ContentAccessException( cause.getMessage( ), cause );
918 throw new ContentAccessException( e.getMessage( ), e );
924 public List<StorageAsset> getRelatedAssets( ArtifactReference reference ) throws ContentNotFoundException, LayoutException, ContentAccessException
930 public String getRepoRoot()
932 return convertUriToPath( repository.getLocation() );
935 private String convertUriToPath( URI uri ) {
936 if (uri.getScheme()==null) {
937 return Paths.get(uri.getPath()).toString();
938 } else if ("file".equals(uri.getScheme())) {
939 return Paths.get(uri).toString();
941 return uri.toString();
946 public ManagedRepository getRepository()
952 * Gather the Available Versions (on disk) for a specific Project Reference, based on filesystem
955 * @return the Set of available versions, based on the project reference.
956 * @throws LayoutException
959 public Set<String> getVersions( ProjectReference reference )
960 throws ContentNotFoundException, LayoutException, ContentAccessException
962 final String path = toPath( reference );
963 final Path projDir = getRepoDir().resolve(toPath(reference));
964 if ( !Files.exists(projDir) )
966 throw new ContentNotFoundException(
967 "Unable to get Versions on a non-existant directory for repository "+getId()+": " + path );
970 if ( !Files.isDirectory(projDir) )
972 throw new ContentNotFoundException(
973 "Unable to get Versions on a non-directory for repository "+getId()+": " + path );
976 final String groupId = reference.getGroupId();
977 final String artifactId = reference.getArtifactId();
978 try(Stream<Path> stream = Files.list(projDir)) {
979 return stream.filter(Files::isDirectory).map(
980 p -> toVersion(groupId, artifactId, p.getFileName().toString())
981 ).filter(this::hasArtifact).map(ref -> ref.getVersion())
982 .collect(Collectors.toSet());
983 } catch (IOException e) {
984 log.error("Could not read directory {}: {}", projDir, e.getMessage(), e);
985 throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, e );
986 } catch (RuntimeException e) {
987 Throwable cause = e.getCause( );
990 if ( cause instanceof LayoutException )
992 throw (LayoutException) cause;
994 log.error("Could not read directory {}: {}", projDir, cause.getMessage(), cause);
995 throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, cause );
998 log.error("Could not read directory {}: {}", projDir, e.getMessage(), e);
999 throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, cause );
1005 public Set<String> getVersions( VersionedReference reference )
1006 throws ContentNotFoundException, ContentAccessException, LayoutException
1008 try(Stream<ArtifactReference> stream = getArtifactStream( reference ))
1010 return stream.filter( Objects::nonNull )
1011 .map( ar -> ar.getVersion( ) )
1012 .collect( Collectors.toSet( ) );
1013 } catch (IOException e) {
1014 final String path = toPath( reference );
1015 log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
1016 throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, e );
1021 public boolean hasContent( ArtifactReference reference ) throws ContentAccessException
1023 StorageAsset artifactFile = toFile( reference );
1024 return artifactFile.exists() && !artifactFile.isContainer();
1028 public boolean hasContent( ProjectReference reference ) throws ContentAccessException
1032 Set<String> versions = getVersions( reference );
1033 return !versions.isEmpty();
1035 catch ( ContentNotFoundException | LayoutException e )
1042 public boolean hasContent( VersionedReference reference ) throws ContentAccessException
1046 return ( getFirstArtifact( reference ) != null );
1048 catch ( LayoutException | ContentNotFoundException e )
1052 catch ( IOException e )
1054 String path = toPath( reference );
1055 log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
1056 throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
1061 public void setRepository( final ManagedRepository repo )
1063 this.repository = repo;
1065 if (repository instanceof EditableManagedRepository) {
1066 ((EditableManagedRepository) repository).setContent(this);
1071 private Path getRepoDir() {
1072 return repository.getAsset( "" ).getFilePath( );
1075 private RepositoryStorage getStorage() {
1076 return repository.getAsset( "" ).getStorage( );
1080 * Convert a path to an artifact reference.
1082 * @param path the path to convert. (relative or full location path)
1083 * @throws LayoutException if the path cannot be converted to an artifact reference.
1086 public ArtifactReference toArtifactReference( String path )
1087 throws LayoutException
1089 String repoPath = convertUriToPath( repository.getLocation() );
1090 if ( ( path != null ) && path.startsWith( repoPath ) && repoPath.length() > 0 )
1092 return super.toArtifactReference( path.substring( repoPath.length() + 1 ) );
1095 if (repoPath!=null) {
1096 while (repoPath.startsWith("/")) {
1097 repoPath = repoPath.substring(1);
1100 return super.toArtifactReference( repoPath );
1105 // The variant with runtime exception for stream usage
1106 private ArtifactReference toArtifactRef(String path) {
1108 return toArtifactReference(path);
1109 } catch (LayoutException e) {
1110 throw new RuntimeException(e);
1117 public StorageAsset toFile( ArtifactReference reference )
1119 return repository.getAsset(toPath(reference));
1123 public StorageAsset toFile( ArchivaArtifact reference )
1125 return repository.getAsset( toPath( reference ) );
1129 public StorageAsset toFile( VersionedReference reference )
1131 return repository.getAsset( toPath( reference ) );
1135 * Get the first Artifact found in the provided VersionedReference location.
1137 * @param reference the reference to the versioned reference to search within
1138 * @return the ArtifactReference to the first artifact located within the versioned reference. or null if
1139 * no artifact was found within the versioned reference.
1140 * @throws java.io.IOException if the versioned reference is invalid (example: doesn't exist, or isn't a directory)
1141 * @throws LayoutException
1143 private ArtifactReference getFirstArtifact( VersionedReference reference )
1144 throws ContentNotFoundException, LayoutException, IOException
1146 try(Stream<ArtifactReference> stream = getArtifactStream( reference ))
1148 return stream.findFirst( ).orElse( null );
1149 } catch (RuntimeException e) {
1150 throw new ContentNotFoundException( e.getMessage( ), e.getCause( ) );
1154 private Stream<ArtifactReference> getArtifactStream(VersionedReference reference) throws ContentNotFoundException, LayoutException, IOException {
1155 final Path repoBase = getRepoDir( );
1156 String path = toMetadataPath( reference );
1157 Path versionDir = repoBase.resolve( path ).getParent();
1158 if ( !Files.exists(versionDir) )
1160 throw new ContentNotFoundException( "Unable to gather the list of artifacts on a non-existant directory: "
1161 + versionDir.toAbsolutePath() );
1164 if ( !Files.isDirectory(versionDir) )
1166 throw new ContentNotFoundException(
1167 "Unable to gather the list of snapshot versions on a non-directory: " + versionDir.toAbsolutePath() );
1169 return Files.list(versionDir).filter(Files::isRegularFile)
1170 .map(p -> repoBase.relativize(p).toString())
1171 .filter(p -> !filetypes.matchesDefaultExclusions(p))
1172 .filter(filetypes::matchesArtifactPattern)
1173 .map(this::toArtifactRef);
1176 public List<ArtifactReference> getArtifacts(VersionedReference reference) throws ContentNotFoundException, LayoutException, ContentAccessException
1178 try (Stream<ArtifactReference> stream = getArtifactStream( reference ))
1180 return stream.collect( Collectors.toList( ) );
1181 } catch ( IOException e )
1183 String path = toPath( reference );
1184 log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
1185 throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
1190 private boolean hasArtifact( VersionedReference reference )
1193 try(Stream<ArtifactReference> stream = getArtifactStream( reference ))
1195 return stream.anyMatch( e -> true );
1196 } catch (ContentNotFoundException e) {
1198 } catch ( LayoutException | IOException e) {
1199 // We throw the runtime exception for better stream handling
1200 throw new RuntimeException(e);
1204 public void setFiletypes( FileTypes filetypes )
1206 this.filetypes = filetypes;
1209 public void setMavenContentHelper( MavenContentHelper contentHelper) {
1210 this.mavenContentHelper = contentHelper;