001package org.apache.archiva.repository.content.maven2; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.common.filelock.FileLockManager; 023import org.apache.archiva.common.utils.PathUtil; 024import org.apache.archiva.configuration.FileTypes; 025import org.apache.archiva.metadata.repository.storage.maven2.ArtifactMappingProvider; 026import org.apache.archiva.metadata.repository.storage.maven2.DefaultArtifactMappingProvider; 027import org.apache.archiva.model.ArchivaArtifact; 028import org.apache.archiva.model.ArtifactReference; 029import org.apache.archiva.model.ProjectReference; 030import org.apache.archiva.model.VersionedReference; 031import org.apache.archiva.repository.ContentNotFoundException; 032import org.apache.archiva.repository.EditableManagedRepository; 033import org.apache.archiva.repository.LayoutException; 034import org.apache.archiva.repository.ManagedRepository; 035import org.apache.archiva.repository.ManagedRepositoryContent; 036import org.apache.archiva.repository.RepositoryException; 037import org.apache.archiva.repository.storage.StorageAsset; 038import org.apache.commons.lang3.StringUtils; 039 040import java.io.IOException; 041import java.net.URI; 042import java.nio.file.Files; 043import java.nio.file.Path; 044import java.nio.file.Paths; 045import java.util.Collections; 046import java.util.HashSet; 047import java.util.List; 048import java.util.Objects; 049import java.util.Set; 050import java.util.stream.Collectors; 051import java.util.stream.Stream; 052 053/** 054 * ManagedDefaultRepositoryContent 055 */ 056public class ManagedDefaultRepositoryContent 057 extends AbstractDefaultRepositoryContent 058 implements ManagedRepositoryContent 059{ 060 061 private FileTypes filetypes; 062 063 public void setFileTypes(FileTypes fileTypes) { 064 this.filetypes = fileTypes; 065 } 066 067 private ManagedRepository repository; 068 069 private Path repoDir; 070 071 FileLockManager lockManager; 072 073 public ManagedDefaultRepositoryContent(ManagedRepository repository, FileTypes fileTypes, FileLockManager lockManager) { 074 super(Collections.singletonList( new DefaultArtifactMappingProvider() )); 075 setFileTypes( fileTypes ); 076 this.lockManager = lockManager; 077 setRepository( repository ); 078 } 079 080 public ManagedDefaultRepositoryContent( ManagedRepository repository, List<? extends ArtifactMappingProvider> artifactMappingProviders, FileTypes fileTypes, FileLockManager lockManager ) 081 { 082 super(artifactMappingProviders==null ? Collections.singletonList( new DefaultArtifactMappingProvider() ) : artifactMappingProviders); 083 setFileTypes( fileTypes ); 084 this.lockManager = lockManager; 085 setRepository( repository ); 086 087 } 088 089 private Path getRepoDir() { 090 return repoDir; 091 } 092 093 094 @Override 095 public void deleteVersion( VersionedReference reference ) 096 { 097 String path = toMetadataPath( reference ); 098 Path projectPath = Paths.get( getRepoRoot(), path ); 099 100 Path projectDir = projectPath.getParent(); 101 if ( Files.exists(projectDir) && Files.isDirectory(projectDir) ) 102 { 103 org.apache.archiva.common.utils.FileUtils.deleteQuietly( projectDir ); 104 } 105 } 106 107 @Override 108 public void deleteProject( String namespace, String projectId ) 109 throws RepositoryException 110 { 111 ArtifactReference artifactReference = new ArtifactReference(); 112 artifactReference.setGroupId( namespace ); 113 artifactReference.setArtifactId( projectId ); 114 String path = toPath( artifactReference ); 115 Path directory = Paths.get( getRepoRoot(), path ); 116 if ( !Files.exists(directory) ) 117 { 118 throw new ContentNotFoundException( "cannot found project " + namespace + ":" + projectId ); 119 } 120 if ( Files.isDirectory(directory) ) 121 { 122 try 123 { 124 org.apache.archiva.common.utils.FileUtils.deleteDirectory( directory ); 125 } 126 catch ( IOException e ) 127 { 128 throw new RepositoryException( e.getMessage(), e ); 129 } 130 } 131 else 132 { 133 log.warn( "project {}:{} is not a directory", namespace, projectId ); 134 } 135 136 } 137 138 @Override 139 public void deleteArtifact( ArtifactReference artifactReference ) 140 { 141 String path = toPath( artifactReference ); 142 Path filePath = Paths.get( getRepoRoot(), path ); 143 144 if ( Files.exists(filePath) ) 145 { 146 org.apache.archiva.common.utils.FileUtils.deleteQuietly( filePath ); 147 } 148 149 Path filePathmd5 = Paths.get( getRepoRoot(), path + ".md5" ); 150 151 if ( Files.exists(filePathmd5) ) 152 { 153 org.apache.archiva.common.utils.FileUtils.deleteQuietly( filePathmd5 ); 154 } 155 156 Path filePathsha1 = Paths.get( getRepoRoot(), path + ".sha1" ); 157 158 if ( Files.exists(filePathsha1) ) 159 { 160 org.apache.archiva.common.utils.FileUtils.deleteQuietly( filePathsha1 ); 161 } 162 } 163 164 @Override 165 public void deleteGroupId( String groupId ) 166 throws ContentNotFoundException 167 { 168 169 String path = StringUtils.replaceChars( groupId, '.', '/' ); 170 171 Path directory = Paths.get( getRepoRoot(), path ); 172 173 if ( Files.exists(directory) ) 174 { 175 try 176 { 177 org.apache.archiva.common.utils.FileUtils.deleteDirectory( directory ); 178 } 179 catch ( IOException e ) 180 { 181 log.warn( "skip error deleting directory {}:", directory, e ); 182 } 183 } 184 } 185 186 @Override 187 public String getId() 188 { 189 return repository.getId(); 190 } 191 192 @Override 193 public Set<ArtifactReference> getRelatedArtifacts( ArtifactReference reference ) 194 throws ContentNotFoundException 195 { 196 StorageAsset artifactFile = toFile( reference ); 197 StorageAsset repoBase = repository.getAsset( "" ); 198 StorageAsset repoDir = artifactFile.getParent(); 199 200 if ( !repoDir.exists()) 201 { 202 throw new ContentNotFoundException( 203 "Unable to get related artifacts using a non-existant directory: " + repoDir.getPath() ); 204 } 205 206 if ( !repoDir.isContainer() ) 207 { 208 throw new ContentNotFoundException( 209 "Unable to get related artifacts using a non-directory: " + repoDir.getPath() ); 210 } 211 212 Set<ArtifactReference> foundArtifacts; 213 214 // First gather up the versions found as artifacts in the managed repository. 215 216 try (Stream<StorageAsset> stream = repoDir.list().stream() ) { 217 foundArtifacts = stream.filter(asset -> !asset.isContainer()).map(path -> { 218 try { 219 ArtifactReference artifact = toArtifactReference(path.getPath()); 220 if( artifact.getGroupId().equals( reference.getGroupId() ) && artifact.getArtifactId().equals( 221 reference.getArtifactId() ) && artifact.getVersion().equals( reference.getVersion() )) { 222 return artifact; 223 } else { 224 return null; 225 } 226 } catch (LayoutException e) { 227 log.debug( "Not processing file that is not an artifact: {}", e.getMessage() ); 228 return null; 229 } 230 }).filter(Objects::nonNull).collect(Collectors.toSet()); 231 } 232 return foundArtifacts; 233 } 234 235 @Override 236 public String getRepoRoot() 237 { 238 return convertUriToPath( repository.getLocation() ); 239 } 240 241 private String convertUriToPath( URI uri ) { 242 if (uri.getScheme()==null) { 243 return Paths.get(uri.getPath()).toString(); 244 } else if ("file".equals(uri.getScheme())) { 245 return Paths.get(uri).toString(); 246 } else { 247 return uri.toString(); 248 } 249 } 250 251 @Override 252 public org.apache.archiva.repository.ManagedRepository getRepository() 253 { 254 return repository; 255 } 256 257 /** 258 * Gather the Available Versions (on disk) for a specific Project Reference, based on filesystem 259 * information. 260 * 261 * @return the Set of available versions, based on the project reference. 262 * @throws LayoutException 263 */ 264 @Override 265 public Set<String> getVersions( ProjectReference reference ) 266 throws ContentNotFoundException, LayoutException 267 { 268 String path = toMetadataPath( reference ); 269 270 int idx = path.lastIndexOf( '/' ); 271 if ( idx > 0 ) 272 { 273 path = path.substring( 0, idx ); 274 } 275 276 Path repoDir = PathUtil.getPathFromUri( repository.getLocation() ).resolve( path ); 277 278 if ( !Files.exists(repoDir) ) 279 { 280 throw new ContentNotFoundException( 281 "Unable to get Versions on a non-existant directory: " + repoDir.toAbsolutePath() ); 282 } 283 284 if ( !Files.isDirectory(repoDir) ) 285 { 286 throw new ContentNotFoundException( 287 "Unable to get Versions on a non-directory: " + repoDir.toAbsolutePath() ); 288 } 289 290 final String groupId = reference.getGroupId(); 291 final String artifactId = reference.getArtifactId(); 292 try(Stream<Path> stream = Files.list(repoDir)) { 293 return stream.filter(Files::isDirectory).map( 294 p -> newVersionedRef(groupId, artifactId, p.getFileName().toString()) 295 ).filter(this::hasArtifact).map(ref -> ref.getVersion()) 296 .collect(Collectors.toSet()); 297 } catch (IOException e) { 298 log.error("Could not read directory {}: {}", repoDir, e.getMessage(), e); 299 } catch (RuntimeException e) { 300 if (e.getCause()!=null && e.getCause() instanceof LayoutException) { 301 throw (LayoutException)e.getCause(); 302 } else { 303 throw e; 304 } 305 } 306 return Collections.emptySet(); 307 } 308 309 static final VersionedReference newVersionedRef(final String groupId, final String artifactId, final String version) { 310 VersionedReference ref = new VersionedReference(); 311 ref.setGroupId(groupId); 312 ref.setArtifactId(artifactId); 313 ref.setVersion(version); 314 return ref; 315 } 316 317 @Override 318 public Set<String> getVersions( VersionedReference reference ) 319 throws ContentNotFoundException 320 { 321 String path = toMetadataPath( reference ); 322 323 int idx = path.lastIndexOf( '/' ); 324 if ( idx > 0 ) 325 { 326 path = path.substring( 0, idx ); 327 } 328 329 Path repoBase = PathUtil.getPathFromUri(repository.getLocation()); 330 Path repoDir = repoBase.resolve( path ); 331 332 if ( !Files.exists(repoDir) ) 333 { 334 throw new ContentNotFoundException( 335 "Unable to get versions on a non-existant directory: " + repoDir.toAbsolutePath() ); 336 } 337 338 if ( !Files.isDirectory(repoDir) ) 339 { 340 throw new ContentNotFoundException( 341 "Unable to get versions on a non-directory: " + repoDir.toAbsolutePath() ); 342 } 343 344 Set<String> foundVersions = new HashSet<>(); 345 346 try(Stream<Path> stream = Files.list(repoDir)) { 347 return stream.filter(Files::isRegularFile) 348 .map(p -> repoBase.relativize(p).toString()) 349 .filter(p -> !filetypes.matchesDefaultExclusions(p)) 350 .filter(filetypes::matchesArtifactPattern) 351 .map(path1 -> { 352 try { 353 return toArtifactReference(path1); 354 } catch (LayoutException e) { 355 log.debug( "Not processing file that is not an artifact: {}", e.getMessage() ); 356 return null; 357 } 358 }).filter(Objects::nonNull) 359 .map(ar -> ar.getVersion()) 360 .collect(Collectors.toSet()); 361 } catch (IOException e) { 362 log.error("Could not read directory {}: {}", repoDir, e.getMessage(), e); 363 } 364 return Collections.emptySet(); 365 } 366 367 @Override 368 public boolean hasContent( ArtifactReference reference ) 369 { 370 StorageAsset artifactFile = toFile( reference ); 371 return artifactFile.exists() && !artifactFile.isContainer(); 372 } 373 374 @Override 375 public boolean hasContent( ProjectReference reference ) 376 { 377 try 378 { 379 Set<String> versions = getVersions( reference ); 380 return !versions.isEmpty(); 381 } 382 catch ( ContentNotFoundException | LayoutException e ) 383 { 384 return false; 385 } 386 } 387 388 @Override 389 public boolean hasContent( VersionedReference reference ) 390 { 391 try 392 { 393 return ( getFirstArtifact( reference ) != null ); 394 } 395 catch ( IOException | LayoutException e ) 396 { 397 return false; 398 } 399 } 400 401 @Override 402 public void setRepository( ManagedRepository repo ) 403 { 404 this.repository = repo; 405 if (repo!=null) { 406 this.repoDir = PathUtil.getPathFromUri(repository.getLocation()); 407 if (repository instanceof EditableManagedRepository) { 408 ((EditableManagedRepository) repository).setContent(this); 409 } 410 } 411 412 } 413 414 /** 415 * Convert a path to an artifact reference. 416 * 417 * @param path the path to convert. (relative or full location path) 418 * @throws LayoutException if the path cannot be converted to an artifact reference. 419 */ 420 @Override 421 public ArtifactReference toArtifactReference( String path ) 422 throws LayoutException 423 { 424 String repoPath = convertUriToPath( repository.getLocation() ); 425 if ( ( path != null ) && path.startsWith( repoPath ) && repoPath.length() > 0 ) 426 { 427 return super.toArtifactReference( path.substring( repoPath.length() + 1 ) ); 428 } else { 429 repoPath = path; 430 if (repoPath!=null) { 431 while (repoPath.startsWith("/")) { 432 repoPath = repoPath.substring(1); 433 } 434 } 435 return super.toArtifactReference( repoPath ); 436 } 437 } 438 439 // The variant with runtime exception for stream usage 440 private ArtifactReference toArtifactRef(String path) { 441 try { 442 return toArtifactReference(path); 443 } catch (LayoutException e) { 444 throw new RuntimeException(e); 445 } 446 } 447 448 449 450 @Override 451 public StorageAsset toFile( ArtifactReference reference ) 452 { 453 return repository.getAsset(toPath(reference)); 454 } 455 456 @Override 457 public StorageAsset toFile( ArchivaArtifact reference ) 458 { 459 return repository.getAsset( toPath( reference ) ); 460 } 461 462 /** 463 * Get the first Artifact found in the provided VersionedReference location. 464 * 465 * @param reference the reference to the versioned reference to search within 466 * @return the ArtifactReference to the first artifact located within the versioned reference. or null if 467 * no artifact was found within the versioned reference. 468 * @throws java.io.IOException if the versioned reference is invalid (example: doesn't exist, or isn't a directory) 469 * @throws LayoutException 470 */ 471 private ArtifactReference getFirstArtifact( VersionedReference reference ) 472 throws LayoutException, IOException 473 { 474 String path = toMetadataPath( reference ); 475 476 int idx = path.lastIndexOf( '/' ); 477 if ( idx > 0 ) 478 { 479 path = path.substring( 0, idx ); 480 } 481 482 Path repoBase = PathUtil.getPathFromUri(repository.getLocation()).toAbsolutePath(); 483 Path repoDir = repoBase.resolve( path ); 484 485 if ( !Files.exists(repoDir) ) 486 { 487 throw new IOException( "Unable to gather the list of snapshot versions on a non-existant directory: " 488 + repoDir.toAbsolutePath() ); 489 } 490 491 if ( !Files.isDirectory(repoDir) ) 492 { 493 throw new IOException( 494 "Unable to gather the list of snapshot versions on a non-directory: " + repoDir.toAbsolutePath() ); 495 } 496 try(Stream<Path> stream = Files.list(repoDir)) { 497 return stream.filter(Files::isRegularFile) 498 .map(p -> repoBase.relativize(p).toString()) 499 .filter(filetypes::matchesArtifactPattern) 500 .map(this::toArtifactRef).findFirst().orElse(null); 501 } catch (RuntimeException e) { 502 if (e.getCause()!=null && e.getCause() instanceof LayoutException) { 503 throw (LayoutException)e.getCause(); 504 } else { 505 throw e; 506 } 507 } 508 509 } 510 511 private boolean hasArtifact( VersionedReference reference ) 512 513 { 514 try 515 { 516 return ( getFirstArtifact( reference ) != null ); 517 } 518 catch ( IOException e ) 519 { 520 return false; 521 } catch (LayoutException e) { 522 // We throw the runtime exception for better stream handling 523 throw new RuntimeException(e); 524 } 525 } 526 527 public void setFiletypes( FileTypes filetypes ) 528 { 529 this.filetypes = filetypes; 530 } 531 532 533}