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.FileLockException;
23 import org.apache.archiva.common.filelock.FileLockManager;
24 import org.apache.archiva.common.filelock.FileLockTimeoutException;
25 import org.apache.archiva.common.filelock.Lock;
26 import org.apache.archiva.common.utils.PathUtil;
27 import org.apache.archiva.configuration.FileTypes;
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.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.RepositoryException;
40 import org.apache.archiva.repository.content.FilesystemAsset;
41 import org.apache.archiva.repository.content.FilesystemStorage;
42 import org.apache.archiva.repository.content.StorageAsset;
43 import org.apache.commons.io.FileUtils;
44 import org.apache.commons.lang.StringUtils;
46 import java.io.FileNotFoundException;
47 import java.io.IOException;
48 import java.io.InputStream;
50 import java.nio.file.Files;
51 import java.nio.file.Path;
52 import java.nio.file.Paths;
53 import java.util.Collections;
54 import java.util.HashSet;
55 import java.util.List;
56 import java.util.Objects;
58 import java.util.function.Consumer;
59 import java.util.stream.Collectors;
60 import java.util.stream.Stream;
63 * ManagedDefaultRepositoryContent
65 public class ManagedDefaultRepositoryContent
66 extends AbstractDefaultRepositoryContent
67 implements ManagedRepositoryContent
70 private final FilesystemStorage storage;
72 private FileTypes filetypes;
74 public void setFileTypes(FileTypes fileTypes) {
75 this.filetypes = fileTypes;
78 private ManagedRepository repository;
82 public ManagedDefaultRepositoryContent(ManagedRepository repository, FileTypes fileTypes, FileLockManager lockManager) {
83 super(Collections.singletonList( new DefaultArtifactMappingProvider() ));
84 setFileTypes( fileTypes );
85 setRepository( repository );
87 storage = new FilesystemStorage(getRepoDir(), lockManager);
88 } catch (IOException e) {
89 log.error("Could not initialize the filesystem storage to repository: {}", getRepoDir());
90 throw new RuntimeException("Fatal error. Could not initialize the filesystem storage for "+getRepoDir());
94 public ManagedDefaultRepositoryContent( ManagedRepository repository, List<? extends ArtifactMappingProvider> artifactMappingProviders, FileTypes fileTypes, FileLockManager lockManager )
96 super(artifactMappingProviders==null ? Collections.singletonList( new DefaultArtifactMappingProvider() ) : artifactMappingProviders);
97 setFileTypes( fileTypes );
98 setRepository( repository );
100 storage = new FilesystemStorage(getRepoDir(), lockManager);
101 } catch (IOException e) {
102 log.error("Could not initialize the filesystem storage to repository: {}", getRepoDir());
103 throw new RuntimeException("Fatal error. Could not initialize the filesystem storage for "+getRepoDir());
107 private Path getRepoDir() {
113 public void deleteVersion( VersionedReference reference )
115 String path = toMetadataPath( reference );
116 Path projectPath = Paths.get( getRepoRoot(), path );
118 Path projectDir = projectPath.getParent();
119 if ( Files.exists(projectDir) && Files.isDirectory(projectDir) )
121 org.apache.archiva.common.utils.FileUtils.deleteQuietly( projectDir );
126 public void deleteProject( String namespace, String projectId )
127 throws RepositoryException
129 ArtifactReference artifactReference = new ArtifactReference();
130 artifactReference.setGroupId( namespace );
131 artifactReference.setArtifactId( projectId );
132 String path = toPath( artifactReference );
133 Path directory = Paths.get( getRepoRoot(), path );
134 if ( !Files.exists(directory) )
136 throw new ContentNotFoundException( "cannot found project " + namespace + ":" + projectId );
138 if ( Files.isDirectory(directory) )
142 org.apache.archiva.common.utils.FileUtils.deleteDirectory( directory );
144 catch ( IOException e )
146 throw new RepositoryException( e.getMessage(), e );
151 log.warn( "project {}:{} is not a directory", namespace, projectId );
157 public void deleteArtifact( ArtifactReference artifactReference )
159 String path = toPath( artifactReference );
160 Path filePath = Paths.get( getRepoRoot(), path );
162 if ( Files.exists(filePath) )
164 org.apache.archiva.common.utils.FileUtils.deleteQuietly( filePath );
167 Path filePathmd5 = Paths.get( getRepoRoot(), path + ".md5" );
169 if ( Files.exists(filePathmd5) )
171 org.apache.archiva.common.utils.FileUtils.deleteQuietly( filePathmd5 );
174 Path filePathsha1 = Paths.get( getRepoRoot(), path + ".sha1" );
176 if ( Files.exists(filePathsha1) )
178 org.apache.archiva.common.utils.FileUtils.deleteQuietly( filePathsha1 );
183 public void deleteGroupId( String groupId )
184 throws ContentNotFoundException
187 String path = StringUtils.replaceChars( groupId, '.', '/' );
189 Path directory = Paths.get( getRepoRoot(), path );
191 if ( Files.exists(directory) )
195 org.apache.archiva.common.utils.FileUtils.deleteDirectory( directory );
197 catch ( IOException e )
199 log.warn( "skip error deleting directory {}:", directory, e );
205 public String getId()
207 return repository.getId();
211 public Set<ArtifactReference> getRelatedArtifacts( ArtifactReference reference )
212 throws ContentNotFoundException
214 Path artifactFile = toFile( reference );
215 Path repoBase = PathUtil.getPathFromUri(repository.getLocation()).toAbsolutePath();
216 Path repoDir = artifactFile.getParent().toAbsolutePath();
218 if ( !Files.exists(repoDir))
220 throw new ContentNotFoundException(
221 "Unable to get related artifacts using a non-existant directory: " + repoDir.toAbsolutePath() );
224 if ( !Files.isDirectory( repoDir ) )
226 throw new ContentNotFoundException(
227 "Unable to get related artifacts using a non-directory: " + repoDir.toAbsolutePath() );
230 Set<ArtifactReference> foundArtifacts;
232 // First gather up the versions found as artifacts in the managed repository.
234 try (Stream<Path> stream = Files.list(repoDir)) {
235 foundArtifacts = stream.filter(Files::isRegularFile).map(path -> {
237 ArtifactReference artifact = toArtifactReference(repoBase.relativize(path).toString());
238 if( artifact.getGroupId().equals( reference.getGroupId() ) && artifact.getArtifactId().equals(
239 reference.getArtifactId() ) && artifact.getVersion().equals( reference.getVersion() )) {
244 } catch (LayoutException e) {
245 log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
248 }).filter(Objects::nonNull).collect(Collectors.toSet());
249 } catch (IOException e) {
250 log.error("Could not read directory {}: {}", repoDir, e.getMessage(), e);
251 return Collections.emptySet();
253 return foundArtifacts;
257 public String getRepoRoot()
259 return convertUriToPath( repository.getLocation() );
262 private String convertUriToPath( URI uri ) {
263 if (uri.getScheme()==null) {
264 return Paths.get(uri.getPath()).toString();
265 } else if ("file".equals(uri.getScheme())) {
266 return Paths.get(uri).toString();
268 return uri.toString();
273 public org.apache.archiva.repository.ManagedRepository getRepository()
279 * Gather the Available Versions (on disk) for a specific Project Reference, based on filesystem
282 * @return the Set of available versions, based on the project reference.
283 * @throws LayoutException
286 public Set<String> getVersions( ProjectReference reference )
287 throws ContentNotFoundException, LayoutException
289 String path = toMetadataPath( reference );
291 int idx = path.lastIndexOf( '/' );
294 path = path.substring( 0, idx );
297 Path repoDir = PathUtil.getPathFromUri( repository.getLocation() ).resolve( path );
299 if ( !Files.exists(repoDir) )
301 throw new ContentNotFoundException(
302 "Unable to get Versions on a non-existant directory: " + repoDir.toAbsolutePath() );
305 if ( !Files.isDirectory(repoDir) )
307 throw new ContentNotFoundException(
308 "Unable to get Versions on a non-directory: " + repoDir.toAbsolutePath() );
311 final String groupId = reference.getGroupId();
312 final String artifactId = reference.getArtifactId();
313 try(Stream<Path> stream = Files.list(repoDir)) {
314 return stream.filter(Files::isDirectory).map(
315 p -> newVersionedRef(groupId, artifactId, p.getFileName().toString())
316 ).filter(this::hasArtifact).map(ref -> ref.getVersion())
317 .collect(Collectors.toSet());
318 } catch (IOException e) {
319 log.error("Could not read directory {}: {}", repoDir, e.getMessage(), e);
320 } catch (RuntimeException e) {
321 if (e.getCause()!=null && e.getCause() instanceof LayoutException) {
322 throw (LayoutException)e.getCause();
327 return Collections.emptySet();
330 static final VersionedReference newVersionedRef(final String groupId, final String artifactId, final String version) {
331 VersionedReference ref = new VersionedReference();
332 ref.setGroupId(groupId);
333 ref.setArtifactId(artifactId);
334 ref.setVersion(version);
339 public Set<String> getVersions( VersionedReference reference )
340 throws ContentNotFoundException
342 String path = toMetadataPath( reference );
344 int idx = path.lastIndexOf( '/' );
347 path = path.substring( 0, idx );
350 Path repoBase = PathUtil.getPathFromUri(repository.getLocation());
351 Path repoDir = repoBase.resolve( path );
353 if ( !Files.exists(repoDir) )
355 throw new ContentNotFoundException(
356 "Unable to get versions on a non-existant directory: " + repoDir.toAbsolutePath() );
359 if ( !Files.isDirectory(repoDir) )
361 throw new ContentNotFoundException(
362 "Unable to get versions on a non-directory: " + repoDir.toAbsolutePath() );
365 Set<String> foundVersions = new HashSet<>();
367 try(Stream<Path> stream = Files.list(repoDir)) {
368 return stream.filter(Files::isRegularFile)
369 .map(p -> repoBase.relativize(p).toString())
370 .filter(p -> !filetypes.matchesDefaultExclusions(p))
371 .filter(filetypes::matchesArtifactPattern)
374 return toArtifactReference(path1);
375 } catch (LayoutException e) {
376 log.debug( "Not processing file that is not an artifact: {}", e.getMessage() );
379 }).filter(Objects::nonNull)
380 .map(ar -> ar.getVersion())
381 .collect(Collectors.toSet());
382 } catch (IOException e) {
383 log.error("Could not read directory {}: {}", repoDir, e.getMessage(), e);
385 return Collections.emptySet();
389 public boolean hasContent( ArtifactReference reference )
391 Path artifactFile = toFile( reference );
392 return Files.exists(artifactFile) && Files.isRegularFile( artifactFile );
396 public boolean hasContent( ProjectReference reference )
400 Set<String> versions = getVersions( reference );
401 return !versions.isEmpty();
403 catch ( ContentNotFoundException | LayoutException e )
410 public boolean hasContent( VersionedReference reference )
414 return ( getFirstArtifact( reference ) != null );
416 catch ( IOException | LayoutException e )
423 public void setRepository( ManagedRepository repo )
425 this.repository = repo;
427 this.repoDir = PathUtil.getPathFromUri(repository.getLocation());
428 if (repository instanceof EditableManagedRepository) {
429 ((EditableManagedRepository) repository).setContent(this);
436 * Convert a path to an artifact reference.
438 * @param path the path to convert. (relative or full location path)
439 * @throws LayoutException if the path cannot be converted to an artifact reference.
442 public ArtifactReference toArtifactReference( String path )
443 throws LayoutException
445 String repoPath = convertUriToPath( repository.getLocation() );
446 if ( ( path != null ) && path.startsWith( repoPath ) && repoPath.length() > 0 )
448 return super.toArtifactReference( path.substring( repoPath.length() + 1 ) );
451 return super.toArtifactReference( path );
454 // The variant with runtime exception for stream usage
455 private ArtifactReference toArtifactRef(String path) {
457 return toArtifactReference(path);
458 } catch (LayoutException e) {
459 throw new RuntimeException(e);
466 public Path toFile( ArtifactReference reference )
468 return PathUtil.getPathFromUri( repository.getLocation()).resolve( toPath( reference ) );
472 public Path toFile( ArchivaArtifact reference )
474 return PathUtil.getPathFromUri( repository.getLocation()).resolve( toPath( reference ) );
478 * Get the first Artifact found in the provided VersionedReference location.
480 * @param reference the reference to the versioned reference to search within
481 * @return the ArtifactReference to the first artifact located within the versioned reference. or null if
482 * no artifact was found within the versioned reference.
483 * @throws java.io.IOException if the versioned reference is invalid (example: doesn't exist, or isn't a directory)
484 * @throws LayoutException
486 private ArtifactReference getFirstArtifact( VersionedReference reference )
487 throws LayoutException, IOException
489 String path = toMetadataPath( reference );
491 int idx = path.lastIndexOf( '/' );
494 path = path.substring( 0, idx );
497 Path repoBase = PathUtil.getPathFromUri(repository.getLocation()).toAbsolutePath();
498 Path repoDir = repoBase.resolve( path );
500 if ( !Files.exists(repoDir) )
502 throw new IOException( "Unable to gather the list of snapshot versions on a non-existant directory: "
503 + repoDir.toAbsolutePath() );
506 if ( !Files.isDirectory(repoDir) )
508 throw new IOException(
509 "Unable to gather the list of snapshot versions on a non-directory: " + repoDir.toAbsolutePath() );
511 try(Stream<Path> stream = Files.list(repoDir)) {
512 return stream.filter(Files::isRegularFile)
513 .map(p -> repoBase.relativize(p).toString())
514 .filter(filetypes::matchesArtifactPattern)
515 .map(this::toArtifactRef).findFirst().orElse(null);
516 } catch (RuntimeException e) {
517 if (e.getCause()!=null && e.getCause() instanceof LayoutException) {
518 throw (LayoutException)e.getCause();
526 private boolean hasArtifact( VersionedReference reference )
531 return ( getFirstArtifact( reference ) != null );
533 catch ( IOException e )
536 } catch (LayoutException e) {
537 // We throw the runtime exception for better stream handling
538 throw new RuntimeException(e);
542 public void setFiletypes( FileTypes filetypes )
544 this.filetypes = filetypes;
549 public void consumeData( StorageAsset asset, Consumer<InputStream> consumerFunction, boolean readLock ) throws IOException
551 storage.consumeData(asset, consumerFunction, readLock);
556 public StorageAsset getAsset( String path )
558 return storage.getAsset(path);
562 public StorageAsset addAsset( String path, boolean container )
564 return storage.addAsset(path, container);
568 public void removeAsset( StorageAsset asset ) throws IOException
570 storage.removeAsset(asset);
574 public StorageAsset moveAsset( StorageAsset origin, String destination ) throws IOException
576 return storage.moveAsset(origin, destination);
580 public StorageAsset copyAsset( StorageAsset origin, String destination ) throws IOException
582 return storage.copyAsset(origin, destination);