1 package org.apache.archiva.proxy;
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 com.google.common.collect.Lists;
23 import org.apache.archiva.admin.model.RepositoryAdminException;
24 import org.apache.archiva.admin.model.beans.NetworkProxy;
25 import org.apache.archiva.admin.model.networkproxy.NetworkProxyAdmin;
26 import org.apache.archiva.configuration.ArchivaConfiguration;
27 import org.apache.archiva.configuration.ConfigurationNames;
28 import org.apache.archiva.configuration.NetworkProxyConfiguration;
29 import org.apache.archiva.configuration.ProxyConnectorConfiguration;
30 import org.apache.archiva.model.ArtifactReference;
31 import org.apache.archiva.model.Keys;
32 import org.apache.archiva.model.RepositoryURL;
33 import org.apache.archiva.policies.DownloadErrorPolicy;
34 import org.apache.archiva.policies.DownloadPolicy;
35 import org.apache.archiva.policies.PolicyConfigurationException;
36 import org.apache.archiva.policies.PolicyViolationException;
37 import org.apache.archiva.policies.PostDownloadPolicy;
38 import org.apache.archiva.policies.PreDownloadPolicy;
39 import org.apache.archiva.policies.ProxyDownloadException;
40 import org.apache.archiva.policies.urlcache.UrlFailureCache;
41 import org.apache.archiva.proxy.common.WagonFactory;
42 import org.apache.archiva.proxy.common.WagonFactoryException;
43 import org.apache.archiva.repository.ManagedRepositoryContent;
44 import org.apache.archiva.repository.RemoteRepositoryContent;
45 import org.apache.archiva.repository.RepositoryContentFactory;
46 import org.apache.archiva.repository.RepositoryException;
47 import org.apache.archiva.repository.RepositoryNotFoundException;
48 import org.apache.archiva.repository.metadata.MetadataTools;
49 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
50 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
51 import org.apache.archiva.scheduler.repository.RepositoryTask;
52 import org.apache.commons.collections.CollectionUtils;
53 import org.apache.commons.io.FileUtils;
54 import org.apache.commons.io.FilenameUtils;
55 import org.apache.commons.lang.StringUtils;
56 import org.apache.commons.lang.SystemUtils;
57 import org.apache.maven.wagon.ConnectionException;
58 import org.apache.maven.wagon.ResourceDoesNotExistException;
59 import org.apache.maven.wagon.Wagon;
60 import org.apache.maven.wagon.WagonException;
61 import org.apache.maven.wagon.authentication.AuthenticationException;
62 import org.apache.maven.wagon.authentication.AuthenticationInfo;
63 import org.apache.maven.wagon.proxy.ProxyInfo;
64 import org.apache.maven.wagon.repository.Repository;
65 import org.codehaus.plexus.registry.Registry;
66 import org.codehaus.plexus.registry.RegistryListener;
67 import org.codehaus.plexus.taskqueue.TaskQueueException;
68 import org.codehaus.plexus.util.SelectorUtils;
69 import org.slf4j.Logger;
70 import org.slf4j.LoggerFactory;
71 import org.springframework.stereotype.Service;
73 import javax.annotation.PostConstruct;
74 import javax.inject.Inject;
75 import javax.inject.Named;
77 import java.io.IOException;
78 import java.util.ArrayList;
79 import java.util.Arrays;
80 import java.util.Collections;
81 import java.util.HashMap;
82 import java.util.LinkedHashMap;
83 import java.util.List;
85 import java.util.Map.Entry;
86 import java.util.Properties;
87 import java.util.concurrent.ConcurrentHashMap;
90 * DefaultRepositoryProxyConnectors
93 * @todo exception handling needs work - "not modified" is not really an exceptional case, and it has more layers than
94 * your average brown onion
96 @Service( "repositoryProxyConnectors#default" )
97 public class DefaultRepositoryProxyConnectors
98 implements RepositoryProxyConnectors, RegistryListener
100 private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class );
106 @Named( value = "archivaConfiguration#default" )
107 private ArchivaConfiguration archivaConfiguration;
113 @Named( value = "repositoryContentFactory#default" )
114 private RepositoryContentFactory repositoryFactory;
120 @Named( value = "metadataTools#default" )
121 private MetadataTools metadataTools;
127 private Map<String, PreDownloadPolicy> preDownloadPolicies;
133 private Map<String, PostDownloadPolicy> postDownloadPolicies;
139 private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
145 private UrlFailureCache urlFailureCache;
147 private Map<String, List<ProxyConnector>> proxyConnectorMap = new HashMap<String, List<ProxyConnector>>();
149 private Map<String, ProxyInfo> networkProxyMap = new ConcurrentHashMap<String, ProxyInfo>();
155 private WagonFactory wagonFactory;
161 @Named( value = "archivaTaskScheduler#repository" )
162 private ArchivaTaskScheduler scheduler;
165 private NetworkProxyAdmin networkProxyAdmin;
168 public void initialize()
170 initConnectorsAndNetworkProxies();
171 archivaConfiguration.addChangeListener( this );
175 @SuppressWarnings( "unchecked" )
176 private void initConnectorsAndNetworkProxies()
179 ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator();
180 this.proxyConnectorMap.clear();
182 List<ProxyConnectorConfiguration> proxyConfigs = archivaConfiguration.getConfiguration().getProxyConnectors();
183 for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs )
185 String key = proxyConfig.getSourceRepoId();
189 // Create connector object.
190 ProxyConnector connector = new ProxyConnector();
192 connector.setSourceRepository(
193 repositoryFactory.getManagedRepositoryContent( proxyConfig.getSourceRepoId() ) );
194 connector.setTargetRepository(
195 repositoryFactory.getRemoteRepositoryContent( proxyConfig.getTargetRepoId() ) );
197 connector.setProxyId( proxyConfig.getProxyId() );
198 connector.setPolicies( proxyConfig.getPolicies() );
199 connector.setOrder( proxyConfig.getOrder() );
200 connector.setDisabled( proxyConfig.isDisabled() );
202 // Copy any blacklist patterns.
203 List<String> blacklist = new ArrayList<String>( 0 );
204 if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) )
206 blacklist.addAll( proxyConfig.getBlackListPatterns() );
208 connector.setBlacklist( blacklist );
210 // Copy any whitelist patterns.
211 List<String> whitelist = new ArrayList<String>( 0 );
212 if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) )
214 whitelist.addAll( proxyConfig.getWhiteListPatterns() );
216 connector.setWhitelist( whitelist );
218 // Get other connectors
219 List<ProxyConnector> connectors = this.proxyConnectorMap.get( key );
220 if ( connectors == null )
222 // Create if we are the first.
223 connectors = new ArrayList<ProxyConnector>( 1 );
226 // Add the connector.
227 connectors.add( connector );
229 // Ensure the list is sorted.
230 Collections.sort( connectors, proxyOrderSorter );
232 // Set the key to the list of connectors.
233 this.proxyConnectorMap.put( key, connectors );
235 catch ( RepositoryNotFoundException e )
237 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
239 catch ( RepositoryException e )
241 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
247 this.networkProxyMap.clear();
249 List<NetworkProxyConfiguration> networkProxies = archivaConfiguration.getConfiguration().getNetworkProxies();
250 for ( NetworkProxyConfiguration networkProxyConfig : networkProxies )
252 String key = networkProxyConfig.getId();
254 ProxyInfo proxy = new ProxyInfo();
256 proxy.setType( networkProxyConfig.getProtocol() );
257 proxy.setHost( networkProxyConfig.getHost() );
258 proxy.setPort( networkProxyConfig.getPort() );
259 proxy.setUserName( networkProxyConfig.getUsername() );
260 proxy.setPassword( networkProxyConfig.getPassword() );
262 this.networkProxyMap.put( key, proxy );
267 public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
268 throws ProxyDownloadException
270 File localFile = toLocalFile( repository, artifact );
272 Properties requestProperties = new Properties();
273 requestProperties.setProperty( "filetype", "artifact" );
274 requestProperties.setProperty( "version", artifact.getVersion() );
275 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
277 List<ProxyConnector> connectors = getProxyConnectors( repository );
278 Map<String, Exception> previousExceptions = new LinkedHashMap<String, Exception>();
279 for ( ProxyConnector connector : connectors )
281 if ( connector.isDisabled() )
286 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
287 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
289 String targetPath = targetRepository.toPath( artifact );
291 if ( SystemUtils.IS_OS_WINDOWS )
293 // toPath use system PATH_SEPARATOR so on windows url are \ which doesn't work very well :-)
294 targetPath = FilenameUtils.separatorsToUnix( targetPath );
299 File downloadedFile =
300 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
303 if ( fileExists( downloadedFile ) )
305 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
306 return downloadedFile;
309 catch ( NotFoundException e )
311 log.debug( "Artifact {} not found on repository \"{}\".", Keys.toKey( artifact ),
312 targetRepository.getRepository().getId() );
314 catch ( NotModifiedException e )
316 log.debug( "Artifact {} not updated on repository \"{}\".", Keys.toKey( artifact ),
317 targetRepository.getRepository().getId() );
319 catch ( ProxyException e )
321 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
322 targetRepository, localFile, e, previousExceptions );
324 catch ( RepositoryAdminException e )
326 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
327 targetRepository, localFile, e, previousExceptions );
331 if ( !previousExceptions.isEmpty() )
333 throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories",
334 previousExceptions );
337 log.debug( "Exhausted all target repositories, artifact {} not found.", Keys.toKey( artifact ) );
342 public File fetchFromProxies( ManagedRepositoryContent repository, String path )
344 File localFile = new File( repository.getRepoRoot(), path );
346 // no update policies for these paths
347 if ( localFile.exists() )
352 Properties requestProperties = new Properties();
353 requestProperties.setProperty( "filetype", "resource" );
354 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
356 List<ProxyConnector> connectors = getProxyConnectors( repository );
357 for ( ProxyConnector connector : connectors )
359 if ( connector.isDisabled() )
364 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
365 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
367 String targetPath = path;
371 File downloadedFile =
372 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
375 if ( fileExists( downloadedFile ) )
377 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
378 return downloadedFile;
381 catch ( NotFoundException e )
383 log.debug( "Resource {} not found on repository \"{}\".", path,
384 targetRepository.getRepository().getId() );
386 catch ( NotModifiedException e )
388 log.debug( "Resource {} not updated on repository \"{}\".", path,
389 targetRepository.getRepository().getId() );
391 catch ( ProxyException e )
394 "Transfer error from repository \"" + targetRepository.getRepository().getId() + "\" for resource "
395 + path + ", continuing to next repository. Error message: " + e.getMessage() );
396 log.debug( "Full stack trace", e );
398 catch ( RepositoryAdminException e )
401 "Transfer error from repository \"" + targetRepository.getRepository().getId() + "\" for resource "
402 + path + ", continuing to next repository. Error message: " + e.getMessage() );
403 log.debug( "Full stack trace", e );
407 log.debug( "Exhausted all target repositories, resource {} not found.", path );
412 public File fetchMetatadaFromProxies( ManagedRepositoryContent repository, String logicalPath )
414 File localFile = new File( repository.getRepoRoot(), logicalPath );
416 Properties requestProperties = new Properties();
417 requestProperties.setProperty( "filetype", "metadata" );
418 boolean metadataNeedsUpdating = false;
419 long originalTimestamp = getLastModified( localFile );
421 List<ProxyConnector> connectors = getProxyConnectors( repository );
422 for ( ProxyConnector connector : connectors )
424 if ( connector.isDisabled() )
429 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
431 File localRepoFile = toLocalRepoFile( repository, targetRepository, logicalPath );
432 long originalMetadataTimestamp = getLastModified( localRepoFile );
436 transferFile( connector, targetRepository, logicalPath, repository, localRepoFile, requestProperties,
439 if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
441 metadataNeedsUpdating = true;
444 catch ( NotFoundException e )
446 if ( log.isDebugEnabled() )
448 log.debug( "Metadata {} not found on remote repository \"{}\".",
449 Lists.<Object>newArrayList( logicalPath, targetRepository.getRepository().getId() ), e );
452 catch ( NotModifiedException e )
454 if ( log.isDebugEnabled() )
456 log.debug( "Metadata {} not updated on remote repository \"{}\".",
457 Lists.<Object>newArrayList( logicalPath, targetRepository.getRepository().getId() ), e );
460 catch ( ProxyException e )
462 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId()
463 + "\" for versioned Metadata " + logicalPath
464 + ", continuing to next repository. Error message: " + e.getMessage() );
465 log.debug( "Full stack trace", e );
467 catch ( RepositoryAdminException e )
469 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId()
470 + "\" for versioned Metadata " + logicalPath
471 + ", continuing to next repository. Error message: " + e.getMessage() );
472 log.debug( "Full stack trace", e );
476 if ( hasBeenUpdated( localFile, originalTimestamp ) )
478 metadataNeedsUpdating = true;
481 if ( metadataNeedsUpdating || !localFile.exists() )
485 metadataTools.updateMetadata( repository, logicalPath );
487 catch ( RepositoryMetadataException e )
489 log.warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
493 if ( fileExists( localFile ) )
501 private long getLastModified( File file )
503 if ( !file.exists() || !file.isFile() )
508 return file.lastModified();
511 private boolean hasBeenUpdated( File file, long originalLastModified )
513 if ( !file.exists() || !file.isFile() )
518 long currentLastModified = getLastModified( file );
519 return ( currentLastModified > originalLastModified );
522 private File toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository,
525 String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath );
526 return new File( repository.getRepoRoot(), repoPath );
530 * Test if the provided ManagedRepositoryContent has any proxies configured for it.
532 public boolean hasProxies( ManagedRepositoryContent repository )
534 synchronized ( this.proxyConnectorMap )
536 return this.proxyConnectorMap.containsKey( repository.getId() );
540 private File toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact )
542 return repository.toFile( artifact );
546 * Simple method to test if the file exists on the local disk.
548 * @param file the file to test. (may be null)
549 * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File.
551 private boolean fileExists( File file )
558 if ( !file.exists() )
563 if ( !file.isFile() )
572 * Perform the transfer of the file.
574 * @param connector the connector configuration to use.
575 * @param remoteRepository the remote repository get the resource from.
576 * @param remotePath the path in the remote repository to the resource to get.
577 * @param repository the managed repository that will hold the file
578 * @param resource the local file to place the downloaded resource into
579 * @param requestProperties the request properties to utilize for policy handling.
580 * @param executeConsumers whether to execute the consumers after proxying
581 * @return the local file that was downloaded, or null if not downloaded.
582 * @throws NotFoundException if the file was not found on the remote repository.
583 * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository, but
584 * the remote resource is not newer than the local File.
585 * @throws ProxyException if transfer was unsuccessful.
587 private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath,
588 ManagedRepositoryContent repository, File resource, Properties requestProperties,
589 boolean executeConsumers )
590 throws ProxyException, NotModifiedException, RepositoryAdminException
592 String url = remoteRepository.getURL().getUrl();
593 if ( !url.endsWith( "/" ) )
597 url = url + remotePath;
598 requestProperties.setProperty( "url", url );
600 // Is a whitelist defined?
601 if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
603 // Path must belong to whitelist.
604 if ( !matchesPattern( remotePath, connector.getWhitelist() ) )
606 log.debug( "Path [{}] is not part of defined whitelist (skipping transfer from repository [{}]).",
607 remotePath, remoteRepository.getRepository().getName() );
612 // Is target path part of blacklist?
613 if ( matchesPattern( remotePath, connector.getBlacklist() ) )
615 log.debug( "Path [{}] is part of blacklist (skipping transfer from repository [{}]).", remotePath,
616 remoteRepository.getRepository().getName() );
620 // Handle pre-download policy
623 validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, resource );
625 catch ( PolicyViolationException e )
627 String emsg = "Transfer not attempted on " + url + " : " + e.getMessage();
628 if ( fileExists( resource ) )
630 log.debug( "{} : using already present local file.", emsg );
640 File tmpResource = null;
642 File workingDirectory = createWorkingDirectory( repository );
648 RepositoryURL repoUrl = remoteRepository.getURL();
649 String protocol = repoUrl.getProtocol();
650 NetworkProxy networkProxy = null;
651 if ( StringUtils.isNotBlank( connector.getProxyId() ) )
653 networkProxy = networkProxyAdmin.getNetworkProxy( connector.getProxyId() );
656 wagon = ( networkProxy != null && networkProxy.isUseNtlm() ) ? wagonFactory.getWagon(
657 "wagon#" + protocol + "-ntlm" ) : wagonFactory.getWagon( "wagon#" + protocol );
660 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
663 boolean connected = connectToRepository( connector, wagon, remoteRepository );
666 tmpResource = new File( workingDirectory, resource.getName() );
667 transferSimpleFile( wagon, remoteRepository, remotePath, repository, resource, tmpResource );
669 // TODO: these should be used to validate the download based on the policies, not always downloaded
671 // save on connections since md5 is rarely used
673 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
676 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
680 catch ( NotFoundException e )
682 urlFailureCache.cacheFailure( url );
685 catch ( NotModifiedException e )
687 // Do not cache url here.
690 catch ( ProxyException e )
692 urlFailureCache.cacheFailure( url );
695 catch ( WagonFactoryException e )
697 throw new ProxyException( e.getMessage(), e );
707 catch ( ConnectionException e )
709 log.warn( "Unable to disconnect wagon.", e );
714 // Handle post-download policies.
717 validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, tmpResource );
719 catch ( PolicyViolationException e )
721 log.warn( "Transfer invalidated from {} : {}", url, e.getMessage() );
722 executeConsumers = false;
723 if ( !fileExists( tmpResource ) )
729 if ( resource != null )
731 synchronized ( resource.getAbsolutePath().intern() )
733 File directory = resource.getParentFile();
734 moveFileIfExists( tmpMd5, directory );
735 moveFileIfExists( tmpSha1, directory );
736 moveFileIfExists( tmpResource, directory );
742 FileUtils.deleteQuietly( workingDirectory );
745 if ( executeConsumers )
747 // Just-in-time update of the index and database by executing the consumers for this artifact
748 //consumers.executeConsumers( connector.getSourceRepository().getRepository(), resource );
749 queueRepositoryTask( connector.getSourceRepository().getRepository().getId(), resource );
755 private void queueRepositoryTask( String repositoryId, File localFile )
757 RepositoryTask task = new RepositoryTask();
758 task.setRepositoryId( repositoryId );
759 task.setResourceFile( localFile );
760 task.setUpdateRelatedArtifacts( true );
761 task.setScanAll( true );
765 scheduler.queueTask( task );
767 catch ( TaskQueueException e )
769 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
775 * Moves the file into repository location if it exists
777 * @param fileToMove this could be either the main artifact, sha1 or md5 checksum file.
778 * @param directory directory to write files to
780 private void moveFileIfExists( File fileToMove, File directory )
781 throws ProxyException
783 if ( fileToMove != null && fileToMove.exists() )
785 File newLocation = new File( directory, fileToMove.getName() );
786 moveTempToTarget( fileToMove, newLocation );
792 * Quietly transfer the checksum file from the remote repository to the local file.
795 * @param wagon the wagon instance (should already be connected) to use.
796 * @param remoteRepository the remote repository to transfer from.
797 * @param remotePath the remote path to the resource to get.
798 * @param repository the managed repository that will hold the file
799 * @param resource the local file that should contain the downloaded contents
800 * @param tmpDirectory the temporary directory to download to
801 * @param ext the type of checksum to transfer (example: ".md5" or ".sha1")
802 * @throws ProxyException if copying the downloaded file into place did not succeed.
804 private File transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
805 ManagedRepositoryContent repository, File resource, File tmpDirectory, String ext )
806 throws ProxyException
808 String url = remoteRepository.getURL().getUrl() + remotePath + ext;
810 // Transfer checksum does not use the policy.
811 if ( urlFailureCache.hasFailedBefore( url ) )
816 File destFile = new File( tmpDirectory, resource.getName() + ext );
820 transferSimpleFile( wagon, remoteRepository, remotePath + ext, repository, resource, destFile );
821 log.debug( "Checksum {} Downloaded: {} to move to {}", Arrays.asList( url, destFile, resource ).toArray() );
823 catch ( NotFoundException e )
825 urlFailureCache.cacheFailure( url );
826 log.debug( "Transfer failed, checksum not found: {}", url );
827 // Consume it, do not pass this on.
829 catch ( NotModifiedException e )
831 log.debug( "Transfer skipped, checksum not modified: {}", url );
832 // Consume it, do not pass this on.
834 catch ( ProxyException e )
836 urlFailureCache.cacheFailure( url );
837 log.warn( "Transfer failed on checksum: " + url + " : " + e.getMessage(), e );
838 // Critical issue, pass it on.
845 * Perform the transfer of the remote file to the local file specified.
847 * @param wagon the wagon instance to use.
848 * @param remoteRepository the remote repository to use
849 * @param remotePath the remote path to attempt to get
850 * @param repository the managed repository that will hold the file
851 * @param origFile the local file to save to
852 * @return The local file that was transfered.
853 * @throws ProxyException if there was a problem moving the downloaded file into place.
854 * @throws WagonException if there was a problem tranfering the file.
856 private void transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
857 ManagedRepositoryContent repository, File origFile, File destFile )
858 throws ProxyException
860 assert ( remotePath != null );
862 // Transfer the file.
865 boolean success = false;
867 if ( !origFile.exists() )
869 log.debug( "Retrieving {} from {}", remotePath, remoteRepository.getRepository().getName() );
870 wagon.get( remotePath, destFile );
873 // You wouldn't get here on failure, a WagonException would have been thrown.
874 log.debug( "Downloaded successfully." );
878 log.debug( "Retrieving {} from {} if updated", remotePath, remoteRepository.getRepository().getName() );
879 success = wagon.getIfNewer( remotePath, destFile, origFile.lastModified() );
882 throw new NotModifiedException(
883 "Not downloaded, as local file is newer than remote side: " + origFile.getAbsolutePath() );
886 if ( destFile.exists() )
888 log.debug( "Downloaded successfully." );
892 catch ( ResourceDoesNotExistException e )
894 throw new NotFoundException(
895 "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(),
898 catch ( WagonException e )
900 // TODO: shouldn't have to drill into the cause, but TransferFailedException is often not descriptive enough
903 "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage();
904 if ( e.getCause() != null )
906 msg += " (cause: " + e.getCause() + ")";
908 throw new ProxyException( msg, e );
913 * Apply the policies.
915 * @param policies the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
916 * @param settings the map of settings for the policies to execute. (Map of String policy keys, to String policy
918 * @param request the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, File)}
920 * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, File)})
922 private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings,
923 Properties request, File localFile )
924 throws PolicyViolationException
926 for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
928 // olamy with spring rolehint is now downloadPolicy#hint
929 // so substring after last # to get the hint as with plexus
930 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
931 DownloadPolicy policy = entry.getValue();
932 String defaultSetting = policy.getDefaultOption();
934 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
936 log.debug( "Applying [{}] policy with [{}]", key, setting );
939 policy.applyPolicy( setting, request, localFile );
941 catch ( PolicyConfigurationException e )
943 log.error( e.getMessage(), e );
948 private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings,
949 Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
950 File localFile, Exception exception, Map<String, Exception> previousExceptions )
951 throws ProxyDownloadException
953 boolean process = true;
954 for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
957 // olamy with spring rolehint is now downloadPolicy#hint
958 // so substring after last # to get the hint as with plexus
959 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
960 DownloadErrorPolicy policy = entry.getValue();
961 String defaultSetting = policy.getDefaultOption();
962 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
964 log.debug( "Applying [{}] policy with [{}]", key, setting );
967 // all policies must approve the exception, any can cancel
968 process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions );
974 catch ( PolicyConfigurationException e )
976 log.error( e.getMessage(), e );
982 // if the exception was queued, don't throw it
983 if ( !previousExceptions.containsKey( content.getId() ) )
985 throw new ProxyDownloadException(
986 "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
987 content.getId(), exception );
992 // if the exception was queued, but cancelled, remove it
993 previousExceptions.remove( content.getId() );
997 "Transfer error from repository \"" + content.getRepository().getId() + "\" for artifact " + Keys.toKey(
998 artifact ) + ", continuing to next repository. Error message: " + exception.getMessage() );
999 log.debug( "Full stack trace", exception );
1003 * Creates a working directory
1006 * @return file location of working directory
1007 * @throws IOException
1009 private File createWorkingDirectory( ManagedRepositoryContent repository )
1011 // TODO: This is ugly - lets actually clean this up when we get the new repository api
1014 File tmpDir = File.createTempFile( ".workingdirectory", null );
1019 catch ( IOException e )
1021 throw new RuntimeException( "Could not create working directory for this request", e );
1026 * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles its
1029 * @param temp The completed download file
1030 * @param target The final location of the downloaded file
1031 * @throws ProxyException when the temp file cannot replace the target file
1033 private void moveTempToTarget( File temp, File target )
1034 throws ProxyException
1036 if ( target.exists() && !target.delete() )
1038 throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() );
1041 target.getParentFile().mkdirs();
1042 if ( !temp.renameTo( target ) )
1044 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
1048 FileUtils.copyFile( temp, target );
1050 catch ( IOException e )
1052 if ( target.exists() )
1054 log.debug( "Tried to copy file {} to {} but file with this name already exists.", temp.getName(),
1055 target.getAbsolutePath() );
1059 throw new ProxyException(
1060 "Cannot copy tmp file " + temp.getAbsolutePath() + " to its final location", e );
1065 FileUtils.deleteQuietly( temp );
1071 * Using wagon, connect to the remote repository.
1073 * @param connector the connector configuration to utilize (for obtaining network proxy configuration from)
1074 * @param wagon the wagon instance to establish the connection on.
1075 * @param remoteRepository the remote repository to connect to.
1076 * @return true if the connection was successful. false if not connected.
1078 private boolean connectToRepository( ProxyConnector connector, Wagon wagon,
1079 RemoteRepositoryContent remoteRepository )
1081 boolean connected = false;
1083 final ProxyInfo networkProxy =
1084 connector.getProxyId() == null ? null : this.networkProxyMap.get( connector.getProxyId() );
1086 if ( log.isDebugEnabled() )
1088 if ( networkProxy != null )
1090 // TODO: move to proxyInfo.toString()
1091 String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
1092 + " to connect to remote repository " + remoteRepository.getURL();
1093 if ( networkProxy.getNonProxyHosts() != null )
1095 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
1097 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
1099 msg += "; as user: " + networkProxy.getUserName();
1105 AuthenticationInfo authInfo = null;
1106 String username = remoteRepository.getRepository().getUserName();
1107 String password = remoteRepository.getRepository().getPassword();
1109 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
1111 log.debug( "Using username {} to connect to remote repository {}", username, remoteRepository.getURL() );
1112 authInfo = new AuthenticationInfo();
1113 authInfo.setUserName( username );
1114 authInfo.setPassword( password );
1117 // Convert seconds to milliseconds
1118 int timeoutInMilliseconds = remoteRepository.getRepository().getTimeout() * 1000;
1120 // Set timeout read and connect
1121 // FIXME olamy having 2 config values
1122 wagon.setReadTimeout( timeoutInMilliseconds );
1123 wagon.setTimeout( timeoutInMilliseconds );
1127 Repository wagonRepository =
1128 new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
1129 wagon.connect( wagonRepository, authInfo, networkProxy );
1132 catch ( ConnectionException e )
1134 log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1137 catch ( AuthenticationException e )
1139 log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1147 * Tests whitelist and blacklist patterns against path.
1149 * @param path the path to test.
1150 * @param patterns the list of patterns to check.
1151 * @return true if the path matches at least 1 pattern in the provided patterns list.
1153 private boolean matchesPattern( String path, List<String> patterns )
1155 if ( CollectionUtils.isEmpty( patterns ) )
1160 if ( !path.startsWith( "/" ) )
1165 for ( String pattern : patterns )
1167 if ( !pattern.startsWith( "/" ) )
1169 pattern = "/" + pattern;
1172 if ( SelectorUtils.matchPath( pattern, path, false ) )
1182 * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
1184 public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository )
1186 synchronized ( this.proxyConnectorMap )
1188 List<ProxyConnector> ret = (List<ProxyConnector>) this.proxyConnectorMap.get( repository.getId() );
1191 return Collections.emptyList();
1194 Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
1199 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1201 if ( ConfigurationNames.isNetworkProxy( propertyName ) || ConfigurationNames.isManagedRepositories(
1202 propertyName ) || ConfigurationNames.isRemoteRepositories( propertyName )
1203 || ConfigurationNames.isProxyConnector( propertyName ) )
1205 initConnectorsAndNetworkProxies();
1209 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1214 public ArchivaConfiguration getArchivaConfiguration()
1216 return archivaConfiguration;
1219 public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
1221 this.archivaConfiguration = archivaConfiguration;
1224 public RepositoryContentFactory getRepositoryFactory()
1226 return repositoryFactory;
1229 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
1231 this.repositoryFactory = repositoryFactory;
1234 public MetadataTools getMetadataTools()
1236 return metadataTools;
1239 public void setMetadataTools( MetadataTools metadataTools )
1241 this.metadataTools = metadataTools;
1244 public UrlFailureCache getUrlFailureCache()
1246 return urlFailureCache;
1249 public void setUrlFailureCache( UrlFailureCache urlFailureCache )
1251 this.urlFailureCache = urlFailureCache;
1254 public WagonFactory getWagonFactory()
1256 return wagonFactory;
1259 public void setWagonFactory( WagonFactory wagonFactory )
1261 this.wagonFactory = wagonFactory;
1264 public Map<String, PreDownloadPolicy> getPreDownloadPolicies()
1266 return preDownloadPolicies;
1269 public void setPreDownloadPolicies( Map<String, PreDownloadPolicy> preDownloadPolicies )
1271 this.preDownloadPolicies = preDownloadPolicies;
1274 public Map<String, PostDownloadPolicy> getPostDownloadPolicies()
1276 return postDownloadPolicies;
1279 public void setPostDownloadPolicies( Map<String, PostDownloadPolicy> postDownloadPolicies )
1281 this.postDownloadPolicies = postDownloadPolicies;
1284 public Map<String, DownloadErrorPolicy> getDownloadErrorPolicies()
1286 return downloadErrorPolicies;
1289 public void setDownloadErrorPolicies( Map<String, DownloadErrorPolicy> downloadErrorPolicies )
1291 this.downloadErrorPolicies = downloadErrorPolicies;