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.repository.maven.metadata.storage.ArtifactMappingProvider;
28 import org.apache.archiva.repository.maven.metadata.storage.DefaultArtifactMappingProvider;
29 import org.apache.archiva.model.ArchivaArtifact;
30 import org.apache.archiva.model.ArtifactReference;
31 import org.apache.archiva.model.ProjectReference;
32 import org.apache.archiva.model.VersionedReference;
33 import org.apache.archiva.repository.ContentAccessException;
34 import org.apache.archiva.repository.ContentNotFoundException;
35 import org.apache.archiva.repository.EditableManagedRepository;
36 import org.apache.archiva.repository.LayoutException;
37 import org.apache.archiva.repository.ManagedRepository;
38 import org.apache.archiva.repository.ManagedRepositoryContent;
39 import org.apache.archiva.repository.content.Artifact;
40 import org.apache.archiva.repository.content.ContentItem;
41 import org.apache.archiva.repository.content.ItemNotFoundException;
42 import org.apache.archiva.repository.content.ItemSelector;
43 import org.apache.archiva.repository.content.Namespace;
44 import org.apache.archiva.repository.content.Project;
45 import org.apache.archiva.repository.content.Version;
46 import org.apache.archiva.repository.content.base.ArchivaNamespace;
47 import org.apache.archiva.repository.content.base.ArchivaProject;
48 import org.apache.archiva.repository.content.base.ArchivaVersion;
49 import org.apache.archiva.repository.content.base.builder.ArtifactOptBuilder;
50 import org.apache.archiva.repository.storage.RepositoryStorage;
51 import org.apache.archiva.repository.storage.StorageAsset;
52 import org.apache.archiva.repository.storage.util.StorageUtil;
53 import org.apache.commons.collections4.map.ReferenceMap;
54 import org.apache.commons.lang3.StringUtils;
56 import javax.inject.Inject;
57 import javax.inject.Named;
58 import java.io.IOException;
60 import java.nio.file.Files;
61 import java.nio.file.Path;
62 import java.nio.file.Paths;
63 import java.util.Collections;
64 import java.util.List;
65 import java.util.Objects;
66 import java.util.Optional;
68 import java.util.function.Predicate;
69 import java.util.regex.Matcher;
70 import java.util.regex.Pattern;
71 import java.util.stream.Collectors;
72 import java.util.stream.Stream;
75 * ManagedDefaultRepositoryContent
77 public class ManagedDefaultRepositoryContent
78 extends AbstractDefaultRepositoryContent
79 implements ManagedRepositoryContent
82 public static final String METADATA_FILENAME = "maven-metadata.xml";
83 private FileTypes filetypes;
85 public void setFileTypes(FileTypes fileTypes) {
86 this.filetypes = fileTypes;
89 private ManagedRepository repository;
91 private FileLockManager lockManager;
94 @Named("repositoryPathTranslator#maven2")
95 private RepositoryPathTranslator pathTranslator;
98 @Named( "metadataReader#maven" )
99 MavenMetadataReader metadataReader;
102 @Named( "MavenContentHelper" )
103 MavenContentHelper mavenContentHelper;
105 public static final String SNAPSHOT = "SNAPSHOT";
107 public static final Pattern UNIQUE_SNAPSHOT_PATTERN = Pattern.compile( "^(SNAPSHOT|[0-9]{8}\\.[0-9]{6}-[0-9]+)(.*)" );
108 public static final Pattern CLASSIFIER_PATTERN = Pattern.compile( "^-([^.]+)(\\..*)" );
110 public static final Pattern TIMESTAMP_PATTERN = Pattern.compile( "^([0-9]{8})\\.([0-9]{6})$" );
112 public static final Pattern GENERIC_SNAPSHOT_PATTERN = Pattern.compile( "^(.*)-" + SNAPSHOT );
115 * We are caching content items in a weak reference map. To avoid always recreating the
116 * the hierarchical structure.
117 * TODO: Better use a object cache? E.g. our spring cache implementation?
119 private ReferenceMap<String, Namespace> namespaceMap = new ReferenceMap<>( );
120 private ReferenceMap<StorageAsset, Project> projectMap = new ReferenceMap<>( );
121 private ReferenceMap<StorageAsset, Version> versionMap = new ReferenceMap<>( );
122 private ReferenceMap<StorageAsset, Artifact> artifactMap = new ReferenceMap<>( );
124 public ManagedDefaultRepositoryContent() {
125 super(Collections.singletonList( new DefaultArtifactMappingProvider() ));
128 public ManagedDefaultRepositoryContent(ManagedRepository repository, FileTypes fileTypes, FileLockManager lockManager) {
129 super(Collections.singletonList( new DefaultArtifactMappingProvider() ));
130 setFileTypes( fileTypes );
131 this.lockManager = lockManager;
132 setRepository( repository );
135 public ManagedDefaultRepositoryContent( ManagedRepository repository, List<? extends ArtifactMappingProvider> artifactMappingProviders, FileTypes fileTypes, FileLockManager lockManager )
137 super(artifactMappingProviders==null ? Collections.singletonList( new DefaultArtifactMappingProvider() ) : artifactMappingProviders);
138 setFileTypes( fileTypes );
139 this.lockManager = lockManager;
140 setRepository( repository );
144 private StorageAsset getAssetByPath(String assetPath) {
145 return getStorage( ).getAsset( assetPath );
148 private StorageAsset getAsset(String namespace) {
149 String namespacePath = formatAsDirectory( namespace.trim() );
150 if (StringUtils.isEmpty( namespacePath )) {
153 return getAssetByPath(namespacePath);
156 private StorageAsset getAsset(String namespace, String project) {
157 return getAsset( namespace ).resolve( project );
160 private StorageAsset getAsset(String namespace, String project, String version) {
161 return getAsset( namespace, project ).resolve( version );
164 private StorageAsset getAsset(String namespace, String project, String version, String fileName) {
165 return getAsset( namespace, project, version ).resolve( fileName );
169 /// ************* End of new generation interface ******************
171 public void deleteItem( ContentItem item ) throws ItemNotFoundException, ContentAccessException
173 final Path baseDirectory = getRepoDir( );
174 final Path itemPath = item.getAsset( ).getFilePath( );
175 if ( !Files.exists( itemPath ) )
177 throw new ItemNotFoundException( "The item " + item.toString() + "does not exist in the repository " + getId( ) );
179 if ( !itemPath.toAbsolutePath().startsWith( baseDirectory.toAbsolutePath() ) )
181 log.error( "The namespace {} to delete from repository {} is not a subdirectory of the repository base.", item, getId( ) );
182 log.error( "Namespace directory: {}", itemPath );
183 log.error( "Repository directory: {}", baseDirectory );
184 throw new ContentAccessException( "Inconsistent directories found. Could not delete namespace." );
188 if (Files.isDirectory( itemPath ))
190 FileUtils.deleteDirectory( itemPath );
192 Files.deleteIfExists( itemPath );
195 catch ( IOException e )
197 log.error( "Could not delete namespace directory {}: {}", itemPath, e.getMessage( ), e );
198 throw new ContentAccessException( "Error occured while deleting namespace " + item + ": " + e.getMessage( ), e );
203 public ContentItem getItem( ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
205 if (selector.hasVersion() && selector.hasArtifactId()) {
206 return getArtifact( selector );
207 } else if (selector.hasProjectId() && selector.hasVersion()) {
208 return getVersion( selector );
209 } else if (selector.hasProjectId()) {
210 return getProject( selector );
212 return getNamespace( selector );
217 public Namespace getNamespace( final ItemSelector namespaceSelector ) throws ContentAccessException, IllegalArgumentException
219 return namespaceMap.computeIfAbsent( namespaceSelector.getNamespace(),
221 StorageAsset nsPath = getAsset( namespace );
222 return ArchivaNamespace.withRepository( this ).withAsset( nsPath ).
223 withNamespace( namespace ).build( );
229 public Project getProject( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
231 if (!selector.hasProjectId()) {
232 throw new IllegalArgumentException( "Project id must be set" );
234 final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ) );
235 return projectMap.computeIfAbsent( path, projectPath -> {
236 final Namespace ns = getNamespace( selector );
237 return ArchivaProject.withAsset( projectPath ).withNamespace( ns ).withId( selector.getProjectId( ) ).build( );
244 public Version getVersion( final ItemSelector selector ) throws ContentAccessException, IllegalArgumentException
246 if (!selector.hasProjectId()) {
247 throw new IllegalArgumentException( "Project id must be set" );
249 if (!selector.hasVersion() ) {
250 throw new IllegalArgumentException( "Version must be set" );
252 final StorageAsset path = getAsset(selector.getNamespace(), selector.getProjectId(), selector.getVersion());
253 return versionMap.computeIfAbsent( path, versionPath -> {
254 final Project project = getProject( selector );
255 return ArchivaVersion.withAsset( path )
256 .withProject( project )
257 .withVersion( selector.getVersion( ) ).build();
265 public Artifact createArtifact(final StorageAsset artifactPath, final ItemSelector selector,
266 final String classifier, final String extension) {
267 Version version = getVersion(selector);
268 ArtifactOptBuilder builder = org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
269 .withVersion( version )
270 .withId( selector.getArtifactId( ) )
271 .withArtifactVersion( mavenContentHelper.getArtifactVersion( artifactPath, selector ) )
272 .withClassifier( classifier );
273 if (selector.hasType()) {
274 builder.withType( selector.getType( ) );
276 return builder.build( );
279 public Namespace getNamespaceFromArtifactPath( final StorageAsset artifactPath) {
280 final StorageAsset namespacePath = artifactPath.getParent( ).getParent( ).getParent( );
281 final String namespace = MavenContentHelper.getNamespaceFromNamespacePath( namespacePath );
282 return namespaceMap.computeIfAbsent( namespace,
283 myNamespace -> ArchivaNamespace.withRepository( this )
284 .withAsset( namespacePath )
285 .withNamespace( namespace )
289 private Project getProjectFromArtifactPath( final StorageAsset artifactPath) {
290 final StorageAsset projectPath = artifactPath.getParent( ).getParent( );
291 return projectMap.computeIfAbsent( projectPath,
292 myProjectPath -> ArchivaProject.withAsset( projectPath )
293 .withNamespace( getNamespaceFromArtifactPath( artifactPath ) )
294 .withId( projectPath.getName( ) ).build( )
298 private Version getVersionFromArtifactPath( final StorageAsset artifactPath) {
299 final StorageAsset versionPath = artifactPath.getParent( );
300 return versionMap.computeIfAbsent( versionPath,
301 myVersionPath -> ArchivaVersion.withAsset( versionPath )
302 .withProject( getProjectFromArtifactPath( artifactPath ) )
303 .withVersion( versionPath.getName( ) ).build( ) );
306 private Artifact getArtifactFromPath(final StorageAsset artifactPath) {
307 final Version version = getVersionFromArtifactPath( artifactPath );
308 final ArtifactInfo info = getArtifactInfoFromPath( version.getVersion(), artifactPath );
309 return artifactMap.computeIfAbsent( artifactPath, myArtifactPath ->
310 org.apache.archiva.repository.content.base.ArchivaArtifact.withAsset( artifactPath )
311 .withVersion( version )
313 .withClassifier( info.classifier )
314 .withRemainder( info.remainder )
315 .withType( info.type )
316 .withArtifactVersion( info.version )
317 .withContentType( info.contentType )
322 private ContentItem getItemFromPath(final StorageAsset itemPath) {
323 if (itemPath.isLeaf()) {
324 return getArtifactFromPath( itemPath );
326 if (versionMap.containsKey( itemPath )) {
327 return versionMap.get( itemPath );
329 if (projectMap.containsKey( itemPath )) {
330 return projectMap.get( itemPath );
332 String ns = MavenContentHelper.getNamespaceFromNamespacePath( itemPath );
333 if (namespaceMap.containsKey( ns )) {
334 return namespaceMap.get( ns );
336 // No cached item, so we have to gather more information:
337 // Check for version directory (contains at least a pom or metadata file)
338 if (itemPath.list( ).stream( ).map(a -> a.getName().toLowerCase()).anyMatch( n ->
340 || n.startsWith( "maven-metadata" )
342 return versionMap.computeIfAbsent( itemPath,
343 myVersionPath -> ArchivaVersion.withAsset( itemPath )
344 .withProject( (Project)getItemFromPath( itemPath.getParent() ) )
345 .withVersion( itemPath.getName() ).build());
347 // We have to dig further and find the next directory with a pom
348 Optional<StorageAsset> foundFile = StorageUtil.newAssetStream( itemPath )
349 .filter( a -> a.getName().toLowerCase().endsWith( ".pom" )
350 || a.getName().toLowerCase().startsWith( "maven-metadata" ) )
352 if (foundFile.isPresent())
355 StorageAsset current = foundFile.get( );
356 while (current.hasParent() && !current.equals(itemPath)) {
358 current = current.getParent( );
360 // Project path if it is one level up from the found file
362 return projectMap.computeIfAbsent( itemPath,
363 myItemPath -> getProjectFromArtifactPath( foundFile.get( ) ) );
365 // All other paths are treated as namespace
366 return namespaceMap.computeIfAbsent( ns,
367 myNamespace -> ArchivaNamespace.withRepository( this )
368 .withAsset( itemPath )
373 // Don't know what to do with it, so we treat it as namespace path
374 return namespaceMap.computeIfAbsent( ns,
375 myNamespace -> ArchivaNamespace.withRepository( this )
376 .withAsset( itemPath )
385 // Simple object to hold artifact information
386 private class ArtifactInfo {
388 private String version;
389 private String extension;
390 private String remainder;
392 private String classifier;
393 private String contentType;
396 private ArtifactInfo getArtifactInfoFromPath(String genericVersion, StorageAsset path) {
397 final ArtifactInfo info = new ArtifactInfo( );
398 info.id = path.getParent( ).getParent( ).getName( );
399 final String fileName = path.getName( );
400 if ( genericVersion.endsWith( "-" + SNAPSHOT ) )
402 String baseVersion = StringUtils.substringBeforeLast( genericVersion, "-" + SNAPSHOT );
403 String prefix = info.id+"-"+baseVersion+"-";
404 if (fileName.startsWith( prefix ))
406 String versionPostfix = StringUtils.removeStart( fileName, prefix );
407 Matcher matcher = UNIQUE_SNAPSHOT_PATTERN.matcher( versionPostfix );
408 if (matcher.matches()) {
409 info.version = baseVersion + "-" + matcher.group( 1 );
410 String newPrefix = prefix + info.version;
411 if (fileName.startsWith( newPrefix ))
413 String classPostfix = StringUtils.removeStart( fileName, newPrefix );
414 Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
415 if (cMatch.matches()) {
416 info.classifier = cMatch.group( 1 );
417 info.remainder = cMatch.group( 2 );
419 info.classifier = "";
420 info.remainder = classPostfix;
423 log.error( "Artifact does not match the maven name pattern {}", path );
424 info.classifier = "";
425 info.remainder = StringUtils.substringAfter( fileName, prefix );
428 log.error( "Artifact does not match the snapshot version pattern {}", path );
430 info.classifier = "";
431 info.remainder = StringUtils.substringAfter( fileName, prefix );
434 log.error( "Artifact does not match the maven name pattern: {}", path );
436 info.classifier = "";
437 info.remainder = StringUtils.substringAfterLast( fileName, "." );
440 String prefix = info.id+"-"+genericVersion;
441 if (fileName.startsWith( prefix ))
443 info.version=genericVersion;
444 String classPostfix = StringUtils.removeStart( fileName, prefix );
445 Matcher cMatch = CLASSIFIER_PATTERN.matcher( classPostfix );
446 if (cMatch.matches()) {
447 info.classifier = cMatch.group( 1 );
448 info.remainder = cMatch.group( 2 );
450 info.classifier = "";
451 info.remainder = classPostfix;
454 log.error( "Artifact does not match the version pattern {}", path );
456 info.classifier = "";
457 info.remainder = StringUtils.substringAfterLast( fileName, "." );
460 info.extension = StringUtils.substringAfterLast( fileName, "." );
461 info.type = MavenContentHelper.getTypeFromClassifierAndExtension( info.classifier, info.extension );
463 info.contentType = Files.probeContentType( path.getFilePath( ) );
464 } catch (IOException e) {
465 info.contentType = "";
473 public Artifact getArtifact( final ItemSelector selector ) throws ContentAccessException
475 if (!selector.hasProjectId( )) {
476 throw new IllegalArgumentException( "Project id must be set" );
478 if (!selector.hasVersion( )) {
479 throw new IllegalArgumentException( "Version must be set" );
481 if (!selector.hasArtifactId( )) {
482 throw new IllegalArgumentException( "Artifact Id must be set" );
484 final StorageAsset artifactDir = getAsset(selector.getNamespace(), selector.getProjectId(),
485 selector.getVersion());
486 final String artifactVersion = mavenContentHelper.getArtifactVersion( artifactDir, selector );
487 final String classifier = MavenContentHelper.getClassifier( selector );
488 final String extension = MavenContentHelper.getArtifactExtension( selector );
489 final String artifactId = StringUtils.isEmpty( selector.getArtifactId( ) ) ? selector.getProjectId( ) : selector.getArtifactId( );
490 final String fileName = MavenContentHelper.getArtifactFileName( artifactId, artifactVersion, classifier, extension );
491 final StorageAsset path = getAsset( selector.getNamespace( ), selector.getProjectId( ),
492 selector.getVersion( ), fileName );
493 return artifactMap.computeIfAbsent( path, artifactPath -> createArtifact( path, selector, classifier, extension ) );
496 private StorageAsset getBasePathFromSelector(ItemSelector selector) {
497 StringBuilder path = new StringBuilder( );
498 if (selector.hasNamespace()) {
499 path.append(String.join( "/", getNamespace( selector ).getNamespacePath( ) ));
501 if (selector.hasProjectId()) {
502 path.append( "/" ).append( selector.getProjectId( ) );
504 if (selector.hasVersion()) {
505 path.append( "/" ).append( selector.getVersion( ) );
507 return getStorage( ).getAsset( path.toString( ) );
511 * File filter to select certain artifacts using the selector data.
513 private Predicate<StorageAsset> getFileFilterFromSelector(final ItemSelector selector) {
514 Predicate<StorageAsset> p = a -> a.isLeaf( );
515 if (selector.hasArtifactId()) {
516 final String pattern = selector.getArtifactId( );
517 p = p.and( a -> StringUtils.startsWithIgnoreCase( a.getName( ), pattern ) );
519 if (selector.hasArtifactVersion()) {
520 final String pattern = selector.getArtifactVersion( );
521 p = p.and( a -> StringUtils.containsIgnoreCase( a.getName( ), pattern ) );
523 if (selector.hasExtension()) {
524 final String pattern = "."+selector.getExtension( );
525 p = p.and( a -> StringUtils.endsWithIgnoreCase( a.getName( ), pattern ) );
526 } else if (selector.hasType()) {
527 final String pattern = "."+ MavenContentHelper.getArtifactExtension( selector );
528 p = p.and( a -> StringUtils.endsWithIgnoreCase( a.getName( ), pattern ) );
530 if (selector.hasClassifier()) {
531 final String pattern = "-" + selector.getClassifier( ) + ".";
532 p = p.and( a -> StringUtils.containsIgnoreCase( a.getName( ), pattern ) );
533 } else if (selector.hasType()) {
534 final String pattern = "-" + MavenContentHelper.getClassifierFromType( selector.getType( ) ) + ".";
535 p = p.and( a -> StringUtils.containsIgnoreCase( a.getName( ).toLowerCase( ), pattern ) );
544 public List<? extends Artifact> getAllArtifacts( ItemSelector selector ) throws ContentAccessException
553 public Stream<? extends Artifact> getAllArtifactStream( ItemSelector selector ) throws ContentAccessException
562 public List<? extends Project> getProjects( Namespace namespace )
571 public List<? extends Version> getVersions( final Project project )
573 StorageAsset asset = getAsset( project.getNamespace( ).getNamespace( ), project.getId( ) );
574 return asset.list( ).stream( ).filter( a -> a.isContainer( ) )
575 .map( a -> ArchivaVersion.withAsset( a )
576 .withProject( project )
577 .withVersion( a.getName() ).build() )
578 .collect( Collectors.toList( ) );
585 public List<? extends Artifact> getArtifacts( ContentItem item )
594 public List<? extends Artifact> getArtifacts( Namespace namespace, boolean recurse )
603 public Stream<? extends Artifact> getArtifactStream( ContentItem item )
612 public Stream<? extends Artifact> getArtifactStream( Namespace namespace, boolean recurse )
621 public boolean hasContent( ItemSelector selector )
630 public void copyArtifact( Path sourceFile, ContentItem destination ) throws IllegalArgumentException
637 * @param path the path string that points to the item
639 * @throws LayoutException
642 public ContentItem toItem( String path ) throws LayoutException
644 ItemSelector selector = getPathParser( ).toItemSelector( path );
645 return getItem( selector );
649 public ContentItem toItem( StorageAsset assetPath ) throws LayoutException
651 return toItem( assetPath.getPath( ) );
654 /// ************* End of new generation interface ******************
657 * Returns a version reference from the coordinates
658 * @param groupId the group id
659 * @param artifactId the artifact id
660 * @param version the version
661 * @return the versioned reference object
664 public VersionedReference toVersion( String groupId, String artifactId, String version ) {
665 return new VersionedReference().groupId( groupId ).artifactId( artifactId ).version( version );
669 public VersionedReference toGenericVersion( ArtifactReference artifactReference )
671 return toVersion( artifactReference.getGroupId( ), artifactReference.getArtifactId( ), VersionUtil.getBaseVersion( artifactReference.getVersion( ) ));
675 * Return the version the artifact is part of
676 * @param artifactReference
679 public VersionedReference toVersion( ArtifactReference artifactReference) {
680 return toVersion( artifactReference.getGroupId( ), artifactReference.getArtifactId( ), artifactReference.getVersion( ) );
684 public ArtifactReference toArtifact( String groupId, String artifactId, String version, String type, String classifier) {
685 return new ArtifactReference( ).groupId( groupId ).artifactId( artifactId ).version( version ).type( type ).classifier( classifier );
690 public void deleteVersion( VersionedReference ref ) throws ContentNotFoundException, ContentAccessException
692 final String path = toPath( ref );
693 final Path deleteTarget = getRepoDir().resolve(path);
694 if ( !Files.exists(deleteTarget) )
696 log.warn( "Version path for repository {} does not exist: {}", getId(), deleteTarget );
697 throw new ContentNotFoundException( "Version not found for repository "+getId()+": "+path );
699 if ( Files.isDirectory(deleteTarget) )
703 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
705 catch ( IOException e )
707 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
708 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
711 log.warn( "Version path for repository {} is not a directory {}", getId(), deleteTarget );
712 throw new ContentNotFoundException( "Version path for repository "+getId()+" is not directory: " + path );
717 public void deleteProject( ProjectReference ref )
718 throws ContentNotFoundException, ContentAccessException
720 final String path = toPath( ref );
721 final Path deleteTarget = getRepoDir( ).resolve( path );
722 if ( !Files.exists(deleteTarget) )
724 log.warn( "Project path for repository {} does not exist: {}", getId(), deleteTarget );
725 throw new ContentNotFoundException( "Project not found for repository "+getId()+": "+path );
727 if ( Files.isDirectory(deleteTarget) )
731 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
733 catch ( IOException e )
735 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
736 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
741 log.warn( "Project path for repository {} is not a directory {}", getId(), deleteTarget );
742 throw new ContentNotFoundException( "Project path for repository "+getId()+" is not directory: " + path );
748 public void deleteProject( String namespace, String projectId ) throws ContentNotFoundException, ContentAccessException
750 this.deleteProject( new ProjectReference().groupId( namespace ).artifactId( projectId ) );
754 public void deleteArtifact( ArtifactReference ref ) throws ContentNotFoundException, ContentAccessException
756 final String path = toPath( ref );
757 final Path repoDir = getRepoDir( );
758 Path deleteTarget = repoDir.resolve( path );
759 if ( Files.exists(deleteTarget) )
763 if (Files.isDirectory( deleteTarget ))
765 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
767 Files.delete( deleteTarget );
770 catch ( IOException e )
772 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
773 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
776 log.warn( "Artifact path for repository {} does not exist: {}", getId(), deleteTarget );
777 throw new ContentNotFoundException( "Artifact not found for repository "+getId()+": "+path );
783 public void deleteGroupId( String groupId )
784 throws ContentNotFoundException, ContentAccessException
786 final String path = toPath( groupId );
787 final Path deleteTarget = getRepoDir( ).resolve( path );
788 if (!Files.exists(deleteTarget)) {
789 log.warn( "Namespace path for repository {} does not exist: {}", getId(), deleteTarget );
790 throw new ContentNotFoundException( "Namespace not found for repository "+getId()+": "+path );
792 if ( Files.isDirectory(deleteTarget) )
796 org.apache.archiva.common.utils.FileUtils.deleteDirectory( deleteTarget );
798 catch ( IOException e )
800 log.error( "Could not delete file path {}: {}", deleteTarget, e.getMessage( ), e );
801 throw new ContentAccessException( "Error while trying to delete path "+path+" from repository "+getId()+": "+e.getMessage( ), e );
804 log.warn( "Namespace path for repository {} is not a directory {}", getId(), deleteTarget );
805 throw new ContentNotFoundException( "Namespace path for repository "+getId()+" is not directory: " + path );
811 public String getId()
813 return repository.getId();
817 public List<ArtifactReference> getRelatedArtifacts( VersionedReference reference )
818 throws ContentNotFoundException, LayoutException, ContentAccessException
820 StorageAsset artifactDir = toFile( reference );
821 if ( !artifactDir.exists())
823 throw new ContentNotFoundException(
824 "Unable to get related artifacts using a non-existant directory: " + artifactDir.getPath() );
827 if ( !artifactDir.isContainer() )
829 throw new ContentNotFoundException(
830 "Unable to get related artifacts using a non-directory: " + artifactDir.getPath() );
833 // First gather up the versions found as artifacts in the managed repository.
835 try (Stream<? extends StorageAsset> stream = artifactDir.list().stream() ) {
836 return stream.filter(asset -> !asset.isContainer()).map(path -> {
838 ArtifactReference artifact = toArtifactReference(path.getPath());
839 if( artifact.getGroupId().equals( reference.getGroupId() ) && artifact.getArtifactId().equals(
840 reference.getArtifactId() ) && artifact.getVersion().equals( reference.getVersion() )) {
845 } catch (LayoutException e) {
846 log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
849 }).filter(Objects::nonNull).collect(Collectors.toList());
850 } catch (RuntimeException e) {
851 Throwable cause = e.getCause( );
853 if (cause instanceof LayoutException) {
854 throw (LayoutException)cause;
857 throw new ContentAccessException( cause.getMessage( ), cause );
860 throw new ContentAccessException( e.getMessage( ), e );
866 * Create the filter for various combinations of classifier and type
868 private Predicate<ArtifactReference> getChecker(ArtifactReference referenceObject, String extension) {
869 // TODO: Check, if extension is the correct parameter here
870 // We compare type with extension which works for artifacts like .jar.md5 but may
871 // be not the best way.
873 if (referenceObject.getClassifier()!=null && referenceObject.getType()!=null) {
874 return ((ArtifactReference a) ->
875 referenceObject.getGroupId().equals( a.getGroupId() )
876 && referenceObject.getArtifactId().equals( a.getArtifactId() )
877 && referenceObject.getVersion( ).equals( a.getVersion( ) )
878 && ( (a.getType()==null)
879 || referenceObject.getType().equals( a.getType() )
880 || a.getType().startsWith(extension) )
881 && referenceObject.getClassifier().equals( a.getClassifier() )
883 } else if (referenceObject.getClassifier()!=null && referenceObject.getType()==null){
884 return ((ArtifactReference a) ->
885 referenceObject.getGroupId().equals( a.getGroupId() )
886 && referenceObject.getArtifactId().equals( a.getArtifactId() )
887 && referenceObject.getVersion( ).equals( a.getVersion( ) )
888 && referenceObject.getClassifier().equals( a.getClassifier() )
890 } else if (referenceObject.getClassifier()==null && referenceObject.getType()!=null){
891 return ((ArtifactReference a) ->
892 referenceObject.getGroupId().equals( a.getGroupId() )
893 && referenceObject.getArtifactId().equals( a.getArtifactId() )
894 && referenceObject.getVersion( ).equals( a.getVersion( ) )
895 && ( (a.getType()==null)
896 || referenceObject.getType().equals( a.getType() )
897 || a.getType().startsWith(extension) )
900 return ((ArtifactReference a) ->
901 referenceObject.getGroupId().equals( a.getGroupId() )
902 && referenceObject.getArtifactId().equals( a.getArtifactId() )
903 && referenceObject.getVersion( ).equals( a.getVersion( ) )
911 public List<ArtifactReference> getRelatedArtifacts( ArtifactReference reference )
912 throws ContentNotFoundException, LayoutException, ContentAccessException
914 if ( StringUtils.isEmpty( reference.getType() ) && StringUtils.isEmpty( reference.getClassifier() ) ) {
915 return getRelatedArtifacts( toVersion( reference ) );
918 StorageAsset artifactFile = toFile( reference );
919 StorageAsset repoDir = artifactFile.getParent();
921 if (!artifactFile.isContainer()) {
922 ext = StringUtils.substringAfterLast( artifactFile.getName(), ".");
927 if ( !repoDir.exists())
929 throw new ContentNotFoundException(
930 "Unable to get related artifacts using a non-existant directory: " + repoDir.getPath() );
933 if ( !repoDir.isContainer() )
935 throw new ContentNotFoundException(
936 "Unable to get related artifacts using a non-directory: " + repoDir.getPath() );
939 // First gather up the versions found as artifacts in the managed repository.
941 try (Stream<? extends StorageAsset> stream = repoDir.list().stream() ) {
942 return stream.filter(
943 asset -> !asset.isContainer())
946 return toArtifactReference(path.getPath());
947 } catch (LayoutException e) {
948 log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
951 }).filter(Objects::nonNull).filter(getChecker( reference, ext )).collect(Collectors.toList());
952 } catch (RuntimeException e) {
953 Throwable cause = e.getCause( );
955 if (cause instanceof LayoutException) {
956 throw (LayoutException)cause;
959 throw new ContentAccessException( cause.getMessage( ), cause );
962 throw new ContentAccessException( e.getMessage( ), e );
968 public List<StorageAsset> getRelatedAssets( ArtifactReference reference ) throws ContentNotFoundException, LayoutException, ContentAccessException
974 public String getRepoRoot()
976 return convertUriToPath( repository.getLocation() );
979 private String convertUriToPath( URI uri ) {
980 if (uri.getScheme()==null) {
981 return Paths.get(uri.getPath()).toString();
982 } else if ("file".equals(uri.getScheme())) {
983 return Paths.get(uri).toString();
985 return uri.toString();
990 public ManagedRepository getRepository()
996 * Gather the Available Versions (on disk) for a specific Project Reference, based on filesystem
999 * @return the Set of available versions, based on the project reference.
1000 * @throws LayoutException
1003 public Set<String> getVersions( ProjectReference reference )
1004 throws ContentNotFoundException, LayoutException, ContentAccessException
1006 final String path = toPath( reference );
1007 final Path projDir = getRepoDir().resolve(toPath(reference));
1008 if ( !Files.exists(projDir) )
1010 throw new ContentNotFoundException(
1011 "Unable to get Versions on a non-existant directory for repository "+getId()+": " + path );
1014 if ( !Files.isDirectory(projDir) )
1016 throw new ContentNotFoundException(
1017 "Unable to get Versions on a non-directory for repository "+getId()+": " + path );
1020 final String groupId = reference.getGroupId();
1021 final String artifactId = reference.getArtifactId();
1022 try(Stream<Path> stream = Files.list(projDir)) {
1023 return stream.filter(Files::isDirectory).map(
1024 p -> toVersion(groupId, artifactId, p.getFileName().toString())
1025 ).filter(this::hasArtifact).map(ref -> ref.getVersion())
1026 .collect(Collectors.toSet());
1027 } catch (IOException e) {
1028 log.error("Could not read directory {}: {}", projDir, e.getMessage(), e);
1029 throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, e );
1030 } catch (RuntimeException e) {
1031 Throwable cause = e.getCause( );
1034 if ( cause instanceof LayoutException )
1036 throw (LayoutException) cause;
1038 log.error("Could not read directory {}: {}", projDir, cause.getMessage(), cause);
1039 throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, cause );
1042 log.error("Could not read directory {}: {}", projDir, e.getMessage(), e);
1043 throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, cause );
1049 public Set<String> getVersions( VersionedReference reference )
1050 throws ContentNotFoundException, ContentAccessException, LayoutException
1052 try(Stream<ArtifactReference> stream = getArtifactStream( reference ))
1054 return stream.filter( Objects::nonNull )
1055 .map( ar -> ar.getVersion( ) )
1056 .collect( Collectors.toSet( ) );
1057 } catch (IOException e) {
1058 final String path = toPath( reference );
1059 log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
1060 throw new ContentAccessException( "Could not read path for repository "+getId()+": "+ path, e );
1065 public boolean hasContent( ArtifactReference reference ) throws ContentAccessException
1067 StorageAsset artifactFile = toFile( reference );
1068 return artifactFile.exists() && !artifactFile.isContainer();
1072 public boolean hasContent( ProjectReference reference ) throws ContentAccessException
1076 Set<String> versions = getVersions( reference );
1077 return !versions.isEmpty();
1079 catch ( ContentNotFoundException | LayoutException e )
1086 public boolean hasContent( VersionedReference reference ) throws ContentAccessException
1090 return ( getFirstArtifact( reference ) != null );
1092 catch ( LayoutException | ContentNotFoundException e )
1096 catch ( IOException e )
1098 String path = toPath( reference );
1099 log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
1100 throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
1105 public void setRepository( final ManagedRepository repo )
1107 this.repository = repo;
1109 if (repository instanceof EditableManagedRepository) {
1110 ((EditableManagedRepository) repository).setContent(this);
1115 private Path getRepoDir() {
1116 return repository.getAsset( "" ).getFilePath( );
1119 private RepositoryStorage getStorage() {
1120 return repository.getAsset( "" ).getStorage( );
1124 * Convert a path to an artifact reference.
1126 * @param path the path to convert. (relative or full location path)
1127 * @throws LayoutException if the path cannot be converted to an artifact reference.
1130 public ArtifactReference toArtifactReference( String path )
1131 throws LayoutException
1133 String repoPath = convertUriToPath( repository.getLocation() );
1134 if ( ( path != null ) && path.startsWith( repoPath ) && repoPath.length() > 0 )
1136 return super.toArtifactReference( path.substring( repoPath.length() + 1 ) );
1139 if (repoPath!=null) {
1140 while (repoPath.startsWith("/")) {
1141 repoPath = repoPath.substring(1);
1144 return super.toArtifactReference( repoPath );
1149 // The variant with runtime exception for stream usage
1150 private ArtifactReference toArtifactRef(String path) {
1152 return toArtifactReference(path);
1153 } catch (LayoutException e) {
1154 throw new RuntimeException(e);
1161 public StorageAsset toFile( ArtifactReference reference )
1163 return repository.getAsset(toPath(reference));
1167 public StorageAsset toFile( ArchivaArtifact reference )
1169 return repository.getAsset( toPath( reference ) );
1173 public StorageAsset toFile( VersionedReference reference )
1175 return repository.getAsset( toPath( reference ) );
1179 * Get the first Artifact found in the provided VersionedReference location.
1181 * @param reference the reference to the versioned reference to search within
1182 * @return the ArtifactReference to the first artifact located within the versioned reference. or null if
1183 * no artifact was found within the versioned reference.
1184 * @throws java.io.IOException if the versioned reference is invalid (example: doesn't exist, or isn't a directory)
1185 * @throws LayoutException
1187 private ArtifactReference getFirstArtifact( VersionedReference reference )
1188 throws ContentNotFoundException, LayoutException, IOException
1190 try(Stream<ArtifactReference> stream = getArtifactStream( reference ))
1192 return stream.findFirst( ).orElse( null );
1193 } catch (RuntimeException e) {
1194 throw new ContentNotFoundException( e.getMessage( ), e.getCause( ) );
1198 private Stream<ArtifactReference> getArtifactStream(VersionedReference reference) throws ContentNotFoundException, LayoutException, IOException {
1199 final Path repoBase = getRepoDir( );
1200 String path = toMetadataPath( reference );
1201 Path versionDir = repoBase.resolve( path ).getParent();
1202 if ( !Files.exists(versionDir) )
1204 throw new ContentNotFoundException( "Unable to gather the list of artifacts on a non-existant directory: "
1205 + versionDir.toAbsolutePath() );
1208 if ( !Files.isDirectory(versionDir) )
1210 throw new ContentNotFoundException(
1211 "Unable to gather the list of snapshot versions on a non-directory: " + versionDir.toAbsolutePath() );
1213 return Files.list(versionDir).filter(Files::isRegularFile)
1214 .map(p -> repoBase.relativize(p).toString())
1215 .filter(p -> !filetypes.matchesDefaultExclusions(p))
1216 .filter(filetypes::matchesArtifactPattern)
1217 .map(this::toArtifactRef);
1220 public List<ArtifactReference> getArtifacts(VersionedReference reference) throws ContentNotFoundException, LayoutException, ContentAccessException
1222 try (Stream<ArtifactReference> stream = getArtifactStream( reference ))
1224 return stream.collect( Collectors.toList( ) );
1225 } catch ( IOException e )
1227 String path = toPath( reference );
1228 log.error("Could not read directory from repository {} - {}: ", getId(), path, e.getMessage(), e);
1229 throw new ContentAccessException( "Could not read path from repository " + getId( ) + ": " + path, e );
1234 private boolean hasArtifact( VersionedReference reference )
1237 try(Stream<ArtifactReference> stream = getArtifactStream( reference ))
1239 return stream.anyMatch( e -> true );
1240 } catch (ContentNotFoundException e) {
1242 } catch ( LayoutException | IOException e) {
1243 // We throw the runtime exception for better stream handling
1244 throw new RuntimeException(e);
1248 public void setFiletypes( FileTypes filetypes )
1250 this.filetypes = filetypes;
1253 public void setMavenContentHelper( MavenContentHelper contentHelper) {
1254 this.mavenContentHelper = contentHelper;