001package org.apache.archiva.proxy; 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.admin.model.RepositoryAdminException; 023import org.apache.archiva.admin.model.beans.NetworkProxy; 024import org.apache.archiva.admin.model.beans.ProxyConnectorRuleType; 025import org.apache.archiva.admin.model.networkproxy.NetworkProxyAdmin; 026import org.apache.archiva.common.filelock.FileLockException; 027import org.apache.archiva.common.filelock.FileLockManager; 028import org.apache.archiva.common.filelock.FileLockTimeoutException; 029import org.apache.archiva.common.filelock.Lock; 030import org.apache.archiva.configuration.ArchivaConfiguration; 031import org.apache.archiva.configuration.Configuration; 032import org.apache.archiva.configuration.ConfigurationNames; 033import org.apache.archiva.configuration.NetworkProxyConfiguration; 034import org.apache.archiva.configuration.ProxyConnectorConfiguration; 035import org.apache.archiva.configuration.ProxyConnectorRuleConfiguration; 036import org.apache.archiva.model.ArtifactReference; 037import org.apache.archiva.model.Keys; 038import org.apache.archiva.model.RepositoryURL; 039import org.apache.archiva.policies.DownloadErrorPolicy; 040import org.apache.archiva.policies.DownloadPolicy; 041import org.apache.archiva.policies.PolicyConfigurationException; 042import org.apache.archiva.policies.PolicyViolationException; 043import org.apache.archiva.policies.PostDownloadPolicy; 044import org.apache.archiva.policies.PreDownloadPolicy; 045import org.apache.archiva.policies.ProxyDownloadException; 046import org.apache.archiva.policies.urlcache.UrlFailureCache; 047import org.apache.archiva.proxy.common.WagonFactory; 048import org.apache.archiva.proxy.common.WagonFactoryException; 049import org.apache.archiva.proxy.common.WagonFactoryRequest; 050import org.apache.archiva.proxy.model.ProxyConnector; 051import org.apache.archiva.proxy.model.ProxyFetchResult; 052import org.apache.archiva.proxy.model.RepositoryProxyConnectors; 053import org.apache.archiva.redback.components.registry.Registry; 054import org.apache.archiva.redback.components.registry.RegistryListener; 055import org.apache.archiva.redback.components.taskqueue.TaskQueueException; 056import org.apache.archiva.repository.ManagedRepository; 057import org.apache.archiva.repository.ManagedRepositoryContent; 058import org.apache.archiva.repository.PasswordCredentials; 059import org.apache.archiva.repository.RemoteRepository; 060import org.apache.archiva.repository.RemoteRepositoryContent; 061import org.apache.archiva.repository.RepositoryContentFactory; 062import org.apache.archiva.repository.RepositoryCredentials; 063import org.apache.archiva.repository.RepositoryRegistry; 064import org.apache.archiva.repository.metadata.MetadataTools; 065import org.apache.archiva.repository.metadata.RepositoryMetadataException; 066import org.apache.archiva.scheduler.ArchivaTaskScheduler; 067import org.apache.archiva.scheduler.repository.model.RepositoryTask; 068import org.apache.commons.collections4.CollectionUtils; 069import org.apache.commons.io.FilenameUtils; 070import org.apache.commons.lang.StringUtils; 071import org.apache.commons.lang.SystemUtils; 072import org.apache.maven.wagon.ConnectionException; 073import org.apache.maven.wagon.ResourceDoesNotExistException; 074import org.apache.maven.wagon.Wagon; 075import org.apache.maven.wagon.WagonException; 076import org.apache.maven.wagon.authentication.AuthenticationException; 077import org.apache.maven.wagon.authentication.AuthenticationInfo; 078import org.apache.maven.wagon.proxy.ProxyInfo; 079import org.apache.maven.wagon.repository.Repository; 080import org.apache.tools.ant.types.selectors.SelectorUtils; 081import org.slf4j.Logger; 082import org.slf4j.LoggerFactory; 083import org.slf4j.MarkerFactory; 084import org.springframework.stereotype.Service; 085 086import javax.annotation.PostConstruct; 087import javax.inject.Inject; 088import javax.inject.Named; 089import java.io.IOException; 090import java.nio.file.Files; 091import java.nio.file.Path; 092import java.nio.file.Paths; 093import java.util.ArrayList; 094import java.util.Collections; 095import java.util.LinkedHashMap; 096import java.util.List; 097import java.util.Map; 098import java.util.Map.Entry; 099import java.util.Properties; 100import java.util.concurrent.ConcurrentHashMap; 101import java.util.concurrent.ConcurrentMap; 102 103/** 104 * DefaultRepositoryProxyConnectors 105 * TODO exception handling needs work - "not modified" is not really an exceptional case, and it has more layers than 106 * your average brown onion 107 */ 108@Service("repositoryProxyConnectors#default") 109public class DefaultRepositoryProxyConnectors 110 implements RepositoryProxyConnectors, RegistryListener 111{ 112 private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class ); 113 114 @Inject 115 @Named(value = "archivaConfiguration#default") 116 private ArchivaConfiguration archivaConfiguration; 117 118 @Inject 119 @Named(value = "repositoryContentFactory#default") 120 private RepositoryContentFactory repositoryFactory; 121 122 @Inject 123 @Named(value = "metadataTools#default") 124 private MetadataTools metadataTools; 125 126 @Inject 127 private Map<String, PreDownloadPolicy> preDownloadPolicies; 128 129 @Inject 130 private Map<String, PostDownloadPolicy> postDownloadPolicies; 131 132 @Inject 133 private Map<String, DownloadErrorPolicy> downloadErrorPolicies; 134 135 @Inject 136 private UrlFailureCache urlFailureCache; 137 138 private ConcurrentMap<String, List<ProxyConnector>> proxyConnectorMap = new ConcurrentHashMap<>(); 139 140 private ConcurrentMap<String, ProxyInfo> networkProxyMap = new ConcurrentHashMap<>(); 141 142 @Inject 143 private WagonFactory wagonFactory; 144 145 @Inject 146 @Named(value = "archivaTaskScheduler#repository") 147 private ArchivaTaskScheduler<RepositoryTask> scheduler; 148 149 @Inject 150 private RepositoryRegistry repositoryRegistry; 151 152 @Inject 153 private NetworkProxyAdmin networkProxyAdmin; 154 155 @Inject 156 @Named(value = "fileLockManager#default") 157 private FileLockManager fileLockManager; 158 159 @PostConstruct 160 public void initialize() 161 { 162 initConnectorsAndNetworkProxies(); 163 archivaConfiguration.addChangeListener( this ); 164 165 } 166 167 @SuppressWarnings("unchecked") 168 private void initConnectorsAndNetworkProxies() 169 { 170 171 ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator(); 172 this.proxyConnectorMap.clear(); 173 174 Configuration configuration = archivaConfiguration.getConfiguration(); 175 176 List<ProxyConnectorRuleConfiguration> allProxyConnectorRuleConfigurations = 177 configuration.getProxyConnectorRuleConfigurations(); 178 179 List<ProxyConnectorConfiguration> proxyConfigs = configuration.getProxyConnectors(); 180 for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs ) 181 { 182 String key = proxyConfig.getSourceRepoId(); 183 184 // Create connector object. 185 ProxyConnector connector = new ProxyConnector(); 186 187 ManagedRepository repo = repositoryRegistry.getManagedRepository( proxyConfig.getSourceRepoId( ) ); 188 if (repo==null) { 189 log.error("Cannot find source repository after config change "+proxyConfig.getSourceRepoId()); 190 continue; 191 } 192 connector.setSourceRepository(repo.getContent()); 193 RemoteRepository rRepo = repositoryRegistry.getRemoteRepository( proxyConfig.getTargetRepoId() ); 194 if (rRepo==null) { 195 log.error("Cannot find target repository after config change "+proxyConfig.getSourceRepoId()); 196 continue; 197 } 198 connector.setTargetRepository(rRepo.getContent()); 199 200 connector.setProxyId( proxyConfig.getProxyId() ); 201 connector.setPolicies( proxyConfig.getPolicies() ); 202 connector.setOrder( proxyConfig.getOrder() ); 203 connector.setDisabled( proxyConfig.isDisabled() ); 204 205 // Copy any blacklist patterns. 206 List<String> blacklist = new ArrayList<>( 0 ); 207 if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) ) 208 { 209 blacklist.addAll( proxyConfig.getBlackListPatterns() ); 210 } 211 connector.setBlacklist( blacklist ); 212 213 // Copy any whitelist patterns. 214 List<String> whitelist = new ArrayList<>( 0 ); 215 if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) ) 216 { 217 whitelist.addAll( proxyConfig.getWhiteListPatterns() ); 218 } 219 connector.setWhitelist( whitelist ); 220 221 List<ProxyConnectorRuleConfiguration> proxyConnectorRuleConfigurations = 222 findProxyConnectorRules( connector.getSourceRepository().getId(), 223 connector.getTargetRepository().getId(), 224 allProxyConnectorRuleConfigurations ); 225 226 if ( !proxyConnectorRuleConfigurations.isEmpty() ) 227 { 228 for ( ProxyConnectorRuleConfiguration proxyConnectorRuleConfiguration : proxyConnectorRuleConfigurations ) 229 { 230 if ( StringUtils.equals( proxyConnectorRuleConfiguration.getRuleType(), 231 ProxyConnectorRuleType.BLACK_LIST.getRuleType() ) ) 232 { 233 connector.getBlacklist().add( proxyConnectorRuleConfiguration.getPattern() ); 234 } 235 236 if ( StringUtils.equals( proxyConnectorRuleConfiguration.getRuleType(), 237 ProxyConnectorRuleType.WHITE_LIST.getRuleType() ) ) 238 { 239 connector.getWhitelist().add( proxyConnectorRuleConfiguration.getPattern() ); 240 } 241 } 242 } 243 244 // Get other connectors 245 List<ProxyConnector> connectors = this.proxyConnectorMap.get( key ); 246 if ( connectors == null ) 247 { 248 // Create if we are the first. 249 connectors = new ArrayList<>( 1 ); 250 } 251 252 // Add the connector. 253 connectors.add( connector ); 254 255 // Ensure the list is sorted. 256 Collections.sort( connectors, proxyOrderSorter ); 257 258 // Set the key to the list of connectors. 259 this.proxyConnectorMap.put( key, connectors ); 260 261 262 } 263 264 this.networkProxyMap.clear(); 265 266 List<NetworkProxyConfiguration> networkProxies = archivaConfiguration.getConfiguration().getNetworkProxies(); 267 for ( NetworkProxyConfiguration networkProxyConfig : networkProxies ) 268 { 269 String key = networkProxyConfig.getId(); 270 271 ProxyInfo proxy = new ProxyInfo(); 272 273 proxy.setType( networkProxyConfig.getProtocol() ); 274 proxy.setHost( networkProxyConfig.getHost() ); 275 proxy.setPort( networkProxyConfig.getPort() ); 276 proxy.setUserName( networkProxyConfig.getUsername() ); 277 proxy.setPassword( networkProxyConfig.getPassword() ); 278 279 this.networkProxyMap.put( key, proxy ); 280 } 281 282 } 283 284 private List<ProxyConnectorRuleConfiguration> findProxyConnectorRules( String sourceRepository, 285 String targetRepository, 286 List<ProxyConnectorRuleConfiguration> all ) 287 { 288 List<ProxyConnectorRuleConfiguration> proxyConnectorRuleConfigurations = new ArrayList<>(); 289 290 for ( ProxyConnectorRuleConfiguration proxyConnectorRuleConfiguration : all ) 291 { 292 for ( ProxyConnectorConfiguration proxyConnector : proxyConnectorRuleConfiguration.getProxyConnectors() ) 293 { 294 if ( StringUtils.equals( sourceRepository, proxyConnector.getSourceRepoId() ) && StringUtils.equals( 295 targetRepository, proxyConnector.getTargetRepoId() ) ) 296 { 297 proxyConnectorRuleConfigurations.add( proxyConnectorRuleConfiguration ); 298 } 299 } 300 } 301 302 return proxyConnectorRuleConfigurations; 303 } 304 305 @Override 306 public Path fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact ) 307 throws ProxyDownloadException 308 { 309 Path localFile = toLocalFile( repository, artifact ); 310 311 Properties requestProperties = new Properties(); 312 requestProperties.setProperty( "filetype", "artifact" ); 313 requestProperties.setProperty( "version", artifact.getVersion() ); 314 requestProperties.setProperty( "managedRepositoryId", repository.getId() ); 315 316 List<ProxyConnector> connectors = getProxyConnectors( repository ); 317 Map<String, Exception> previousExceptions = new LinkedHashMap<>(); 318 for ( ProxyConnector connector : connectors ) 319 { 320 if ( connector.isDisabled() ) 321 { 322 continue; 323 } 324 325 RemoteRepositoryContent targetRepository = connector.getTargetRepository(); 326 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() ); 327 328 String targetPath = targetRepository.toPath( artifact ); 329 330 if ( SystemUtils.IS_OS_WINDOWS ) 331 { 332 // toPath use system PATH_SEPARATOR so on windows url are \ which doesn't work very well :-) 333 targetPath = FilenameUtils.separatorsToUnix( targetPath ); 334 } 335 336 try 337 { 338 Path downloadedFile = 339 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties, 340 true ); 341 342 if ( fileExists( downloadedFile ) ) 343 { 344 log.debug( "Successfully transferred: {}", downloadedFile.toAbsolutePath() ); 345 return downloadedFile; 346 } 347 } 348 catch ( NotFoundException e ) 349 { 350 log.debug( "Artifact {} not found on repository \"{}\".", Keys.toKey( artifact ), 351 targetRepository.getRepository().getId() ); 352 } 353 catch ( NotModifiedException e ) 354 { 355 log.debug( "Artifact {} not updated on repository \"{}\".", Keys.toKey( artifact ), 356 targetRepository.getRepository().getId() ); 357 } 358 catch ( ProxyException | RepositoryAdminException e ) 359 { 360 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact, 361 targetRepository, localFile, e, previousExceptions ); 362 } 363 } 364 365 if ( !previousExceptions.isEmpty() ) 366 { 367 throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories", 368 previousExceptions ); 369 } 370 371 log.debug( "Exhausted all target repositories, artifact {} not found.", Keys.toKey( artifact ) ); 372 373 return null; 374 } 375 376 @Override 377 public Path fetchFromProxies( ManagedRepositoryContent repository, String path ) 378 { 379 Path localFile = Paths.get( repository.getRepoRoot(), path ); 380 381 // no update policies for these paths 382 if ( Files.exists(localFile) ) 383 { 384 return null; 385 } 386 387 Properties requestProperties = new Properties(); 388 requestProperties.setProperty( "filetype", "resource" ); 389 requestProperties.setProperty( "managedRepositoryId", repository.getId() ); 390 391 List<ProxyConnector> connectors = getProxyConnectors( repository ); 392 for ( ProxyConnector connector : connectors ) 393 { 394 if ( connector.isDisabled() ) 395 { 396 continue; 397 } 398 399 RemoteRepositoryContent targetRepository = connector.getTargetRepository(); 400 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() ); 401 402 String targetPath = path; 403 404 try 405 { 406 Path downloadedFile = 407 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties, 408 false ); 409 410 if ( fileExists( downloadedFile ) ) 411 { 412 log.debug( "Successfully transferred: {}", downloadedFile.toAbsolutePath() ); 413 return downloadedFile; 414 } 415 } 416 catch ( NotFoundException e ) 417 { 418 log.debug( "Resource {} not found on repository \"{}\".", path, 419 targetRepository.getRepository().getId() ); 420 } 421 catch ( NotModifiedException e ) 422 { 423 log.debug( "Resource {} not updated on repository \"{}\".", path, 424 targetRepository.getRepository().getId() ); 425 } 426 catch ( ProxyException e ) 427 { 428 log.warn( 429 "Transfer error from repository {} for resource {}, continuing to next repository. Error message: {}", 430 targetRepository.getRepository().getId(), path, e.getMessage() ); 431 log.debug( MarkerFactory.getDetachedMarker( "transfer.error" ), 432 "Transfer error from repository \"{}" 433 + "\" for resource {}, continuing to next repository. Error message: {}", 434 targetRepository.getRepository().getId(), path, e.getMessage(), e ); 435 } 436 catch ( RepositoryAdminException e ) 437 { 438 log.debug( MarkerFactory.getDetachedMarker( "transfer.error" ), 439 "Transfer error from repository {} for resource {}, continuing to next repository. Error message: {}", 440 targetRepository.getRepository().getId(), path, e.getMessage(), e ); 441 log.debug( MarkerFactory.getDetachedMarker( "transfer.error" ), "Full stack trace", e ); 442 } 443 } 444 445 log.debug( "Exhausted all target repositories, resource {} not found.", path ); 446 447 return null; 448 } 449 450 @Override 451 public ProxyFetchResult fetchMetadataFromProxies( ManagedRepositoryContent repository, String logicalPath ) 452 { 453 Path localFile = Paths.get( repository.getRepoRoot(), logicalPath ); 454 455 Properties requestProperties = new Properties(); 456 requestProperties.setProperty( "filetype", "metadata" ); 457 boolean metadataNeedsUpdating = false; 458 long originalTimestamp = getLastModified( localFile ); 459 460 List<ProxyConnector> connectors = new ArrayList<>( getProxyConnectors( repository ) ); 461 for ( ProxyConnector connector : connectors ) 462 { 463 if ( connector.isDisabled() ) 464 { 465 continue; 466 } 467 468 RemoteRepositoryContent targetRepository = connector.getTargetRepository(); 469 470 Path localRepoFile = toLocalRepoFile( repository, targetRepository, logicalPath ); 471 long originalMetadataTimestamp = getLastModified( localRepoFile ); 472 473 try 474 { 475 transferFile( connector, targetRepository, logicalPath, repository, localRepoFile, requestProperties, 476 true ); 477 478 if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) ) 479 { 480 metadataNeedsUpdating = true; 481 } 482 } 483 catch ( NotFoundException e ) 484 { 485 486 log.debug( "Metadata {} not found on remote repository '{}'.", logicalPath, 487 targetRepository.getRepository().getId(), e ); 488 489 } 490 catch ( NotModifiedException e ) 491 { 492 493 log.debug( "Metadata {} not updated on remote repository '{}'.", logicalPath, 494 targetRepository.getRepository().getId(), e ); 495 496 } 497 catch ( ProxyException | RepositoryAdminException e ) 498 { 499 log.warn( 500 "Transfer error from repository {} for versioned Metadata {}, continuing to next repository. Error message: {}", 501 targetRepository.getRepository().getId(), logicalPath, e.getMessage() ); 502 log.debug( "Full stack trace", e ); 503 } 504 } 505 506 if ( hasBeenUpdated( localFile, originalTimestamp ) ) 507 { 508 metadataNeedsUpdating = true; 509 } 510 511 if ( metadataNeedsUpdating || !Files.exists(localFile)) 512 { 513 try 514 { 515 metadataTools.updateMetadata( repository, logicalPath ); 516 } 517 catch ( RepositoryMetadataException e ) 518 { 519 log.warn( "Unable to update metadata {}:{}", localFile.toAbsolutePath(), e.getMessage(), e ); 520 } 521 522 } 523 524 if ( fileExists( localFile ) ) 525 { 526 return new ProxyFetchResult( localFile, metadataNeedsUpdating ); 527 } 528 529 return new ProxyFetchResult( null, false ); 530 } 531 532 /** 533 * @param connector 534 * @param remoteRepository 535 * @param tmpMd5 536 * @param tmpSha1 537 * @param tmpResource 538 * @param url 539 * @param remotePath 540 * @param resource 541 * @param workingDirectory 542 * @param repository 543 * @throws ProxyException 544 * @throws NotModifiedException 545 * @throws org.apache.archiva.admin.model.RepositoryAdminException 546 */ 547 protected void transferResources( ProxyConnector connector, RemoteRepositoryContent remoteRepository, Path tmpMd5, 548 Path tmpSha1, Path tmpResource, String url, String remotePath, Path resource, 549 Path workingDirectory, ManagedRepositoryContent repository ) 550 throws ProxyException, NotModifiedException, RepositoryAdminException 551 { 552 Wagon wagon = null; 553 try 554 { 555 RepositoryURL repoUrl = remoteRepository.getURL(); 556 String protocol = repoUrl.getProtocol(); 557 NetworkProxy networkProxy = null; 558 if ( StringUtils.isNotBlank( connector.getProxyId() ) ) 559 { 560 networkProxy = networkProxyAdmin.getNetworkProxy( connector.getProxyId() ); 561 } 562 WagonFactoryRequest wagonFactoryRequest = new WagonFactoryRequest( "wagon#" + protocol, 563 remoteRepository.getRepository().getExtraHeaders() ).networkProxy( 564 networkProxy ); 565 wagon = wagonFactory.getWagon( wagonFactoryRequest ); 566 if ( wagon == null ) 567 { 568 throw new ProxyException( "Unsupported target repository protocol: " + protocol ); 569 } 570 571 if ( wagon == null ) 572 { 573 throw new ProxyException( "Unsupported target repository protocol: " + protocol ); 574 } 575 576 boolean connected = connectToRepository( connector, wagon, remoteRepository ); 577 if ( connected ) 578 { 579 transferArtifact( wagon, remoteRepository, remotePath, repository, resource, workingDirectory, 580 tmpResource ); 581 582 // TODO: these should be used to validate the download based on the policies, not always downloaded 583 // to 584 // save on connections since md5 is rarely used 585 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory, ".sha1", 586 tmpSha1 ); 587 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory, ".md5", 588 tmpMd5 ); 589 } 590 } 591 catch ( NotFoundException e ) 592 { 593 urlFailureCache.cacheFailure( url ); 594 throw e; 595 } 596 catch ( NotModifiedException e ) 597 { 598 // Do not cache url here. 599 throw e; 600 } 601 catch ( ProxyException e ) 602 { 603 urlFailureCache.cacheFailure( url ); 604 throw e; 605 } 606 catch ( WagonFactoryException e ) 607 { 608 throw new ProxyException( e.getMessage(), e ); 609 } 610 finally 611 { 612 if ( wagon != null ) 613 { 614 try 615 { 616 wagon.disconnect(); 617 } 618 catch ( ConnectionException e ) 619 { 620 log.warn( "Unable to disconnect wagon.", e ); 621 } 622 } 623 } 624 } 625 626 private void transferArtifact( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath, 627 ManagedRepositoryContent repository, Path resource, Path tmpDirectory, 628 Path destFile ) 629 throws ProxyException 630 { 631 transferSimpleFile( wagon, remoteRepository, remotePath, repository, resource, destFile ); 632 } 633 634 private long getLastModified( Path file ) 635 { 636 if ( !Files.exists(file) || !Files.isRegularFile(file) ) 637 { 638 return 0; 639 } 640 641 try 642 { 643 return Files.getLastModifiedTime(file).toMillis(); 644 } 645 catch ( IOException e ) 646 { 647 log.error("Could get the modified time of file {}", file.toAbsolutePath()); 648 return 0; 649 } 650 } 651 652 private boolean hasBeenUpdated( Path file, long originalLastModified ) 653 { 654 if ( !Files.exists(file) || !Files.isRegularFile(file) ) 655 { 656 return false; 657 } 658 659 long currentLastModified = getLastModified( file ); 660 return ( currentLastModified > originalLastModified ); 661 } 662 663 private Path toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository, 664 String targetPath ) 665 { 666 String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath ); 667 return Paths.get( repository.getRepoRoot(), repoPath ); 668 } 669 670 /** 671 * Test if the provided ManagedRepositoryContent has any proxies configured for it. 672 */ 673 @Override 674 public boolean hasProxies( ManagedRepositoryContent repository ) 675 { 676 synchronized ( this.proxyConnectorMap ) 677 { 678 return this.proxyConnectorMap.containsKey( repository.getId() ); 679 } 680 } 681 682 private Path toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact ) 683 { 684 return repository.toFile( artifact ); 685 } 686 687 /** 688 * Simple method to test if the file exists on the local disk. 689 * 690 * @param file the file to test. (may be null) 691 * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File. 692 */ 693 private boolean fileExists( Path file ) 694 { 695 if ( file == null ) 696 { 697 return false; 698 } 699 700 if ( !Files.exists(file)) 701 { 702 return false; 703 } 704 705 return Files.isRegularFile(file); 706 } 707 708 /** 709 * Perform the transfer of the file. 710 * 711 * @param connector the connector configuration to use. 712 * @param remoteRepository the remote repository get the resource from. 713 * @param remotePath the path in the remote repository to the resource to get. 714 * @param repository the managed repository that will hold the file 715 * @param resource the local file to place the downloaded resource into 716 * @param requestProperties the request properties to utilize for policy handling. 717 * @param executeConsumers whether to execute the consumers after proxying 718 * @return the local file that was downloaded, or null if not downloaded. 719 * @throws NotFoundException if the file was not found on the remote repository. 720 * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository, but 721 * the remote resource is not newer than the local File. 722 * @throws ProxyException if transfer was unsuccessful. 723 */ 724 private Path transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath, 725 ManagedRepositoryContent repository, Path resource, Properties requestProperties, 726 boolean executeConsumers ) 727 throws ProxyException, NotModifiedException, RepositoryAdminException 728 { 729 String url = remoteRepository.getURL().getUrl(); 730 if ( !url.endsWith( "/" ) ) 731 { 732 url = url + "/"; 733 } 734 url = url + remotePath; 735 requestProperties.setProperty( "url", url ); 736 737 // Is a whitelist defined? 738 if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) ) 739 { 740 // Path must belong to whitelist. 741 if ( !matchesPattern( remotePath, connector.getWhitelist() ) ) 742 { 743 log.debug( "Path [{}] is not part of defined whitelist (skipping transfer from repository [{}]).", 744 remotePath, remoteRepository.getRepository().getName() ); 745 return null; 746 } 747 } 748 749 // Is target path part of blacklist? 750 if ( matchesPattern( remotePath, connector.getBlacklist() ) ) 751 { 752 log.debug( "Path [{}] is part of blacklist (skipping transfer from repository [{}]).", remotePath, 753 remoteRepository.getRepository().getName() ); 754 return null; 755 } 756 757 // Handle pre-download policy 758 try 759 { 760 validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, resource ); 761 } 762 catch ( PolicyViolationException e ) 763 { 764 String emsg = "Transfer not attempted on " + url + " : " + e.getMessage(); 765 if ( fileExists( resource ) ) 766 { 767 log.debug( "{} : using already present local file.", emsg ); 768 return resource; 769 } 770 771 log.debug( emsg ); 772 return null; 773 } 774 775 Path workingDirectory = createWorkingDirectory( repository ); 776 Path tmpResource = workingDirectory.resolve(resource.getFileName()); 777 Path tmpMd5 = workingDirectory.resolve(resource.getFileName().toString() + ".md5" ); 778 Path tmpSha1 = workingDirectory.resolve( resource.getFileName().toString() + ".sha1" ); 779 780 try 781 { 782 783 transferResources( connector, remoteRepository, tmpMd5, tmpSha1, tmpResource, url, remotePath, resource, 784 workingDirectory, repository ); 785 786 // Handle post-download policies. 787 try 788 { 789 validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, tmpResource ); 790 } 791 catch ( PolicyViolationException e ) 792 { 793 log.warn( "Transfer invalidated from {} : {}", url, e.getMessage() ); 794 executeConsumers = false; 795 if ( !fileExists( tmpResource ) ) 796 { 797 resource = null; 798 } 799 } 800 801 if ( resource != null ) 802 { 803 synchronized ( resource.toAbsolutePath().toString().intern() ) 804 { 805 Path directory = resource.getParent(); 806 moveFileIfExists( tmpMd5, directory ); 807 moveFileIfExists( tmpSha1, directory ); 808 moveFileIfExists( tmpResource, directory ); 809 } 810 } 811 } 812 finally 813 { 814 org.apache.archiva.common.utils.FileUtils.deleteQuietly( workingDirectory ); 815 } 816 817 if ( executeConsumers ) 818 { 819 // Just-in-time update of the index and database by executing the consumers for this artifact 820 //consumers.executeConsumers( connector.getSourceRepository().getRepository(), resource ); 821 queueRepositoryTask( connector.getSourceRepository().getRepository().getId(), resource ); 822 } 823 824 return resource; 825 } 826 827 private void queueRepositoryTask( String repositoryId, Path localFile ) 828 { 829 RepositoryTask task = new RepositoryTask(); 830 task.setRepositoryId( repositoryId ); 831 task.setResourceFile( localFile ); 832 task.setUpdateRelatedArtifacts( true ); 833 task.setScanAll( true ); 834 835 try 836 { 837 scheduler.queueTask( task ); 838 } 839 catch ( TaskQueueException e ) 840 { 841 log.error( "Unable to queue repository task to execute consumers on resource file ['{}" 842 + "'].", localFile.getFileName() ); 843 } 844 } 845 846 /** 847 * Moves the file into repository location if it exists 848 * 849 * @param fileToMove this could be either the main artifact, sha1 or md5 checksum file. 850 * @param directory directory to write files to 851 */ 852 private void moveFileIfExists( Path fileToMove, Path directory ) 853 throws ProxyException 854 { 855 if ( fileToMove != null && Files.exists(fileToMove) ) 856 { 857 Path newLocation = directory.resolve(fileToMove.getFileName()); 858 moveTempToTarget( fileToMove, newLocation ); 859 } 860 } 861 862 /** 863 * <p> 864 * Quietly transfer the checksum file from the remote repository to the local file. 865 * </p> 866 * 867 * @param wagon the wagon instance (should already be connected) to use. 868 * @param remoteRepository the remote repository to transfer from. 869 * @param remotePath the remote path to the resource to get. 870 * @param repository the managed repository that will hold the file 871 * @param resource the local file that should contain the downloaded contents 872 * @param tmpDirectory the temporary directory to download to 873 * @param ext the type of checksum to transfer (example: ".md5" or ".sha1") 874 * @throws ProxyException if copying the downloaded file into place did not succeed. 875 */ 876 private void transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath, 877 ManagedRepositoryContent repository, Path resource, Path tmpDirectory, String ext, 878 Path destFile ) 879 throws ProxyException 880 { 881 String url = remoteRepository.getURL().getUrl() + remotePath + ext; 882 883 // Transfer checksum does not use the policy. 884 if ( urlFailureCache.hasFailedBefore( url ) ) 885 { 886 return; 887 } 888 889 try 890 { 891 transferSimpleFile( wagon, remoteRepository, remotePath + ext, repository, resource, destFile ); 892 log.debug( "Checksum {} Downloaded: {} to move to {}", url, destFile, resource ); 893 } 894 catch ( NotFoundException e ) 895 { 896 urlFailureCache.cacheFailure( url ); 897 log.debug( "Transfer failed, checksum not found: {}", url ); 898 // Consume it, do not pass this on. 899 } 900 catch ( NotModifiedException e ) 901 { 902 log.debug( "Transfer skipped, checksum not modified: {}", url ); 903 // Consume it, do not pass this on. 904 } 905 catch ( ProxyException e ) 906 { 907 urlFailureCache.cacheFailure( url ); 908 log.warn( "Transfer failed on checksum: {} : {}", url, e.getMessage(), e ); 909 // Critical issue, pass it on. 910 throw e; 911 } 912 } 913 914 /** 915 * Perform the transfer of the remote file to the local file specified. 916 * 917 * @param wagon the wagon instance to use. 918 * @param remoteRepository the remote repository to use 919 * @param remotePath the remote path to attempt to get 920 * @param repository the managed repository that will hold the file 921 * @param origFile the local file to save to 922 * @throws ProxyException if there was a problem moving the downloaded file into place. 923 */ 924 private void transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath, 925 ManagedRepositoryContent repository, Path origFile, Path destFile ) 926 throws ProxyException 927 { 928 assert ( remotePath != null ); 929 930 // Transfer the file. 931 try 932 { 933 boolean success = false; 934 935 if ( !Files.exists(origFile)) 936 { 937 log.debug( "Retrieving {} from {}", remotePath, remoteRepository.getRepository().getName() ); 938 wagon.get( addParameters( remotePath, remoteRepository.getRepository() ), destFile.toFile() ); 939 success = true; 940 941 // You wouldn't get here on failure, a WagonException would have been thrown. 942 log.debug( "Downloaded successfully." ); 943 } 944 else 945 { 946 log.debug( "Retrieving {} from {} if updated", remotePath, remoteRepository.getRepository().getName() ); 947 try 948 { 949 success = wagon.getIfNewer( addParameters( remotePath, remoteRepository.getRepository() ), destFile.toFile(), 950 Files.getLastModifiedTime(origFile).toMillis()); 951 } 952 catch ( IOException e ) 953 { 954 throw new ProxyException( "Failed to the modification time of "+origFile.toAbsolutePath() ); 955 } 956 if ( !success ) 957 { 958 throw new NotModifiedException( 959 "Not downloaded, as local file is newer than remote side: " + origFile.toAbsolutePath() ); 960 } 961 962 if ( Files.exists(destFile)) 963 { 964 log.debug( "Downloaded successfully." ); 965 } 966 } 967 } 968 catch ( ResourceDoesNotExistException e ) 969 { 970 throw new NotFoundException( 971 "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(), 972 e ); 973 } 974 catch ( WagonException e ) 975 { 976 // TODO: shouldn't have to drill into the cause, but TransferFailedException is often not descriptive enough 977 978 String msg = 979 "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage(); 980 if ( e.getCause() != null ) 981 { 982 msg += " (cause: " + e.getCause() + ")"; 983 } 984 throw new ProxyException( msg, e ); 985 } 986 } 987 988 /** 989 * Apply the policies. 990 * 991 * @param policies the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects) 992 * @param settings the map of settings for the policies to execute. (Map of String policy keys, to String policy 993 * setting) 994 * @param request the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, Path)} 995 * ) 996 * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, Path)}) 997 * @throws PolicyViolationException 998 */ 999 private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings, 1000 Properties request, Path localFile ) 1001 throws PolicyViolationException 1002 { 1003 for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() ) 1004 { 1005 // olamy with spring rolehint is now downloadPolicy#hint 1006 // so substring after last # to get the hint as with plexus 1007 String key = StringUtils.substringAfterLast( entry.getKey(), "#" ); 1008 DownloadPolicy policy = entry.getValue(); 1009 String defaultSetting = policy.getDefaultOption(); 1010 1011 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting ); 1012 1013 log.debug( "Applying [{}] policy with [{}]", key, setting ); 1014 try 1015 { 1016 policy.applyPolicy( setting, request, localFile ); 1017 } 1018 catch ( PolicyConfigurationException e ) 1019 { 1020 log.error( e.getMessage(), e ); 1021 } 1022 } 1023 } 1024 1025 private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings, 1026 Properties request, ArtifactReference artifact, RemoteRepositoryContent content, 1027 Path localFile, Exception exception, Map<String, Exception> previousExceptions ) 1028 throws ProxyDownloadException 1029 { 1030 boolean process = true; 1031 for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() ) 1032 { 1033 1034 // olamy with spring rolehint is now downloadPolicy#hint 1035 // so substring after last # to get the hint as with plexus 1036 String key = StringUtils.substringAfterLast( entry.getKey(), "#" ); 1037 DownloadErrorPolicy policy = entry.getValue(); 1038 String defaultSetting = policy.getDefaultOption(); 1039 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting ); 1040 1041 log.debug( "Applying [{}] policy with [{}]", key, setting ); 1042 try 1043 { 1044 // all policies must approve the exception, any can cancel 1045 process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions ); 1046 if ( !process ) 1047 { 1048 break; 1049 } 1050 } 1051 catch ( PolicyConfigurationException e ) 1052 { 1053 log.error( e.getMessage(), e ); 1054 } 1055 } 1056 1057 if ( process ) 1058 { 1059 // if the exception was queued, don't throw it 1060 if ( !previousExceptions.containsKey( content.getId() ) ) 1061 { 1062 throw new ProxyDownloadException( 1063 "An error occurred in downloading from the remote repository, and the policy is to fail immediately", 1064 content.getId(), exception ); 1065 } 1066 } 1067 else 1068 { 1069 // if the exception was queued, but cancelled, remove it 1070 previousExceptions.remove( content.getId() ); 1071 } 1072 1073 log.warn( 1074 "Transfer error from repository {} for artifact {} , continuing to next repository. Error message: {}", 1075 content.getRepository().getId(), Keys.toKey( artifact ), exception.getMessage() ); 1076 log.debug( "Full stack trace", exception ); 1077 } 1078 1079 /** 1080 * Creates a working directory 1081 * 1082 * @param repository 1083 * @return file location of working directory 1084 */ 1085 private Path createWorkingDirectory( ManagedRepositoryContent repository ) 1086 { 1087 try 1088 { 1089 return Files.createTempDirectory( "temp" ); 1090 } 1091 catch ( IOException e ) 1092 { 1093 throw new RuntimeException( e.getMessage(), e ); 1094 } 1095 1096 } 1097 1098 /** 1099 * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles its 1100 * downloaded files. 1101 * 1102 * @param temp The completed download file 1103 * @param target The final location of the downloaded file 1104 * @throws ProxyException when the temp file cannot replace the target file 1105 */ 1106 private void moveTempToTarget( Path temp, Path target ) 1107 throws ProxyException 1108 { 1109 1110 Lock lock; 1111 try 1112 { 1113 lock = fileLockManager.writeFileLock( target ); 1114 try { 1115 Files.deleteIfExists(lock.getFile()); 1116 } catch (IOException e) { 1117 throw new ProxyException( "Unable to overwrite existing target file: " + target.toAbsolutePath() ); 1118 } 1119 1120 try { 1121 Files.createDirectories(lock.getFile().getParent()); 1122 } catch (IOException e) { 1123 throw new ProxyException("Unable to create parent directory "+lock.getFile().getParent()); 1124 } 1125 1126 try 1127 { 1128 Files.move(temp, lock.getFile() ); 1129 } 1130 catch ( IOException e ) 1131 { 1132 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." ); 1133 1134 try 1135 { 1136 Files.copy( temp, lock.getFile()); 1137 } 1138 catch ( IOException e2 ) 1139 { 1140 if ( Files.exists(lock.getFile()) ) 1141 { 1142 log.debug( "Tried to copy file {} to {} but file with this name already exists.", 1143 temp.getFileName(), lock.getFile().toAbsolutePath() ); 1144 } 1145 else 1146 { 1147 throw new ProxyException( 1148 "Cannot copy tmp file " + temp.toAbsolutePath() + " to its final location", e2 ); 1149 } 1150 } 1151 finally 1152 { 1153 org.apache.archiva.common.utils.FileUtils.deleteQuietly( temp ); 1154 } 1155 } 1156 1157 } 1158 catch ( FileLockException | FileLockTimeoutException e ) 1159 { 1160 throw new ProxyException( e.getMessage(), e ); 1161 } 1162 } 1163 1164 /** 1165 * Using wagon, connect to the remote repository. 1166 * 1167 * @param connector the connector configuration to utilize (for obtaining network proxy configuration from) 1168 * @param wagon the wagon instance to establish the connection on. 1169 * @param remoteRepository the remote repository to connect to. 1170 * @return true if the connection was successful. false if not connected. 1171 */ 1172 private boolean connectToRepository( ProxyConnector connector, Wagon wagon, 1173 RemoteRepositoryContent remoteRepository ) 1174 { 1175 boolean connected = false; 1176 1177 final ProxyInfo networkProxy = 1178 connector.getProxyId() == null ? null : this.networkProxyMap.get( connector.getProxyId() ); 1179 1180 if ( log.isDebugEnabled() ) 1181 { 1182 if ( networkProxy != null ) 1183 { 1184 // TODO: move to proxyInfo.toString() 1185 String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort() 1186 + " to connect to remote repository " + remoteRepository.getURL(); 1187 if ( networkProxy.getNonProxyHosts() != null ) 1188 { 1189 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts(); 1190 } 1191 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) ) 1192 { 1193 msg += "; as user: " + networkProxy.getUserName(); 1194 } 1195 log.debug( msg ); 1196 } 1197 } 1198 1199 AuthenticationInfo authInfo = null; 1200 String username = ""; 1201 String password = ""; 1202 RepositoryCredentials repCred = remoteRepository.getRepository().getLoginCredentials(); 1203 if (repCred!=null && repCred instanceof PasswordCredentials) { 1204 PasswordCredentials pwdCred = (PasswordCredentials) repCred; 1205 username = pwdCred.getUsername(); 1206 password = pwdCred.getPassword()==null ? "" : new String(pwdCred.getPassword()); 1207 } 1208 1209 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) ) 1210 { 1211 log.debug( "Using username {} to connect to remote repository {}", username, remoteRepository.getURL() ); 1212 authInfo = new AuthenticationInfo(); 1213 authInfo.setUserName( username ); 1214 authInfo.setPassword( password ); 1215 } 1216 1217 // Convert seconds to milliseconds 1218 1219 long timeoutInMilliseconds = remoteRepository.getRepository().getTimeout().toMillis(); 1220 1221 // Set timeout read and connect 1222 // FIXME olamy having 2 config values 1223 wagon.setReadTimeout( (int) timeoutInMilliseconds ); 1224 wagon.setTimeout( (int) timeoutInMilliseconds ); 1225 1226 try 1227 { 1228 Repository wagonRepository = 1229 new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() ); 1230 wagon.connect( wagonRepository, authInfo, networkProxy ); 1231 connected = true; 1232 } 1233 catch ( ConnectionException | AuthenticationException e ) 1234 { 1235 log.warn( "Could not connect to {}: {}", remoteRepository.getRepository().getName(), e.getMessage() ); 1236 connected = false; 1237 } 1238 1239 return connected; 1240 } 1241 1242 /** 1243 * Tests whitelist and blacklist patterns against path. 1244 * 1245 * @param path the path to test. 1246 * @param patterns the list of patterns to check. 1247 * @return true if the path matches at least 1 pattern in the provided patterns list. 1248 */ 1249 private boolean matchesPattern( String path, List<String> patterns ) 1250 { 1251 if ( CollectionUtils.isEmpty( patterns ) ) 1252 { 1253 return false; 1254 } 1255 1256 if ( !path.startsWith( "/" ) ) 1257 { 1258 path = "/" + path; 1259 } 1260 1261 for ( String pattern : patterns ) 1262 { 1263 if ( !pattern.startsWith( "/" ) ) 1264 { 1265 pattern = "/" + pattern; 1266 } 1267 1268 if ( SelectorUtils.matchPath( pattern, path, false ) ) 1269 { 1270 return true; 1271 } 1272 } 1273 1274 return false; 1275 } 1276 1277 /** 1278 * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477 1279 */ 1280 @Override 1281 public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository ) 1282 { 1283 1284 if ( !this.proxyConnectorMap.containsKey( repository.getId() ) ) 1285 { 1286 return Collections.emptyList(); 1287 } 1288 List<ProxyConnector> ret = new ArrayList<>( this.proxyConnectorMap.get( repository.getId() ) ); 1289 1290 Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() ); 1291 return ret; 1292 1293 } 1294 1295 @Override 1296 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue ) 1297 { 1298 if ( ConfigurationNames.isNetworkProxy( propertyName ) // 1299 || ConfigurationNames.isManagedRepositories( propertyName ) // 1300 || ConfigurationNames.isRemoteRepositories( propertyName ) // 1301 || ConfigurationNames.isProxyConnector( propertyName ) ) // 1302 { 1303 initConnectorsAndNetworkProxies(); 1304 } 1305 } 1306 1307 protected String addParameters( String path, RemoteRepository remoteRepository ) 1308 { 1309 if ( remoteRepository.getExtraParameters().isEmpty() ) 1310 { 1311 return path; 1312 } 1313 1314 boolean question = false; 1315 1316 StringBuilder res = new StringBuilder( path == null ? "" : path ); 1317 1318 for ( Entry<String, String> entry : remoteRepository.getExtraParameters().entrySet() ) 1319 { 1320 if ( !question ) 1321 { 1322 res.append( '?' ).append( entry.getKey() ).append( '=' ).append( entry.getValue() ); 1323 } 1324 } 1325 1326 return res.toString(); 1327 } 1328 1329 1330 @Override 1331 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue ) 1332 { 1333 /* do nothing */ 1334 } 1335 1336 public ArchivaConfiguration getArchivaConfiguration() 1337 { 1338 return archivaConfiguration; 1339 } 1340 1341 public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration ) 1342 { 1343 this.archivaConfiguration = archivaConfiguration; 1344 } 1345 1346 public RepositoryContentFactory getRepositoryFactory() 1347 { 1348 return repositoryFactory; 1349 } 1350 1351 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory ) 1352 { 1353 this.repositoryFactory = repositoryFactory; 1354 } 1355 1356 public MetadataTools getMetadataTools() 1357 { 1358 return metadataTools; 1359 } 1360 1361 public void setMetadataTools( MetadataTools metadataTools ) 1362 { 1363 this.metadataTools = metadataTools; 1364 } 1365 1366 public UrlFailureCache getUrlFailureCache() 1367 { 1368 return urlFailureCache; 1369 } 1370 1371 public void setUrlFailureCache( UrlFailureCache urlFailureCache ) 1372 { 1373 this.urlFailureCache = urlFailureCache; 1374 } 1375 1376 public WagonFactory getWagonFactory() 1377 { 1378 return wagonFactory; 1379 } 1380 1381 public void setWagonFactory( WagonFactory wagonFactory ) 1382 { 1383 this.wagonFactory = wagonFactory; 1384 } 1385 1386 public Map<String, PreDownloadPolicy> getPreDownloadPolicies() 1387 { 1388 return preDownloadPolicies; 1389 } 1390 1391 public void setPreDownloadPolicies( Map<String, PreDownloadPolicy> preDownloadPolicies ) 1392 { 1393 this.preDownloadPolicies = preDownloadPolicies; 1394 } 1395 1396 public Map<String, PostDownloadPolicy> getPostDownloadPolicies() 1397 { 1398 return postDownloadPolicies; 1399 } 1400 1401 public void setPostDownloadPolicies( Map<String, PostDownloadPolicy> postDownloadPolicies ) 1402 { 1403 this.postDownloadPolicies = postDownloadPolicies; 1404 } 1405 1406 public Map<String, DownloadErrorPolicy> getDownloadErrorPolicies() 1407 { 1408 return downloadErrorPolicies; 1409 } 1410 1411 public void setDownloadErrorPolicies( Map<String, DownloadErrorPolicy> downloadErrorPolicies ) 1412 { 1413 this.downloadErrorPolicies = downloadErrorPolicies; 1414 } 1415}