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.io.Files;
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.redback.components.registry.Registry;
44 import org.apache.archiva.redback.components.registry.RegistryListener;
45 import org.apache.archiva.redback.components.taskqueue.TaskQueueException;
46 import org.apache.archiva.repository.ManagedRepositoryContent;
47 import org.apache.archiva.repository.RemoteRepositoryContent;
48 import org.apache.archiva.repository.RepositoryContentFactory;
49 import org.apache.archiva.repository.RepositoryException;
50 import org.apache.archiva.repository.RepositoryNotFoundException;
51 import org.apache.archiva.repository.metadata.MetadataTools;
52 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
53 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
54 import org.apache.archiva.scheduler.repository.RepositoryTask;
55 import org.apache.commons.collections.CollectionUtils;
56 import org.apache.commons.io.FileUtils;
57 import org.apache.commons.io.FilenameUtils;
58 import org.apache.commons.lang.StringUtils;
59 import org.apache.commons.lang.SystemUtils;
60 import org.apache.maven.wagon.ConnectionException;
61 import org.apache.maven.wagon.ResourceDoesNotExistException;
62 import org.apache.maven.wagon.Wagon;
63 import org.apache.maven.wagon.WagonException;
64 import org.apache.maven.wagon.authentication.AuthenticationException;
65 import org.apache.maven.wagon.authentication.AuthenticationInfo;
66 import org.apache.maven.wagon.proxy.ProxyInfo;
67 import org.apache.maven.wagon.repository.Repository;
68 import org.apache.tools.ant.types.selectors.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
92 * @todo exception handling needs work - "not modified" is not really an exceptional case, and it has more layers than
93 * your average brown onion
95 @Service ( "repositoryProxyConnectors#default" )
96 public class DefaultRepositoryProxyConnectors
97 implements RepositoryProxyConnectors, RegistryListener
99 private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class );
105 @Named ( value = "archivaConfiguration#default" )
106 private ArchivaConfiguration archivaConfiguration;
112 @Named ( value = "repositoryContentFactory#default" )
113 private RepositoryContentFactory repositoryFactory;
119 @Named ( value = "metadataTools#default" )
120 private MetadataTools metadataTools;
126 private Map<String, PreDownloadPolicy> preDownloadPolicies;
132 private Map<String, PostDownloadPolicy> postDownloadPolicies;
138 private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
144 private UrlFailureCache urlFailureCache;
146 private Map<String, List<ProxyConnector>> proxyConnectorMap = new HashMap<String, List<ProxyConnector>>();
148 private Map<String, ProxyInfo> networkProxyMap = new ConcurrentHashMap<String, ProxyInfo>();
154 private WagonFactory wagonFactory;
160 @Named ( value = "archivaTaskScheduler#repository" )
161 private ArchivaTaskScheduler scheduler;
164 private NetworkProxyAdmin networkProxyAdmin;
167 public void initialize()
169 initConnectorsAndNetworkProxies();
170 archivaConfiguration.addChangeListener( this );
174 @SuppressWarnings ( "unchecked" )
175 private void initConnectorsAndNetworkProxies()
178 ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator();
179 this.proxyConnectorMap.clear();
181 List<ProxyConnectorConfiguration> proxyConfigs = archivaConfiguration.getConfiguration().getProxyConnectors();
182 for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs )
184 String key = proxyConfig.getSourceRepoId();
188 // Create connector object.
189 ProxyConnector connector = new ProxyConnector();
191 connector.setSourceRepository(
192 repositoryFactory.getManagedRepositoryContent( proxyConfig.getSourceRepoId() ) );
193 connector.setTargetRepository(
194 repositoryFactory.getRemoteRepositoryContent( proxyConfig.getTargetRepoId() ) );
196 connector.setProxyId( proxyConfig.getProxyId() );
197 connector.setPolicies( proxyConfig.getPolicies() );
198 connector.setOrder( proxyConfig.getOrder() );
199 connector.setDisabled( proxyConfig.isDisabled() );
201 // Copy any blacklist patterns.
202 List<String> blacklist = new ArrayList<String>( 0 );
203 if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) )
205 blacklist.addAll( proxyConfig.getBlackListPatterns() );
207 connector.setBlacklist( blacklist );
209 // Copy any whitelist patterns.
210 List<String> whitelist = new ArrayList<String>( 0 );
211 if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) )
213 whitelist.addAll( proxyConfig.getWhiteListPatterns() );
215 connector.setWhitelist( whitelist );
217 // Get other connectors
218 List<ProxyConnector> connectors = this.proxyConnectorMap.get( key );
219 if ( connectors == null )
221 // Create if we are the first.
222 connectors = new ArrayList<ProxyConnector>( 1 );
225 // Add the connector.
226 connectors.add( connector );
228 // Ensure the list is sorted.
229 Collections.sort( connectors, proxyOrderSorter );
231 // Set the key to the list of connectors.
232 this.proxyConnectorMap.put( key, connectors );
234 catch ( RepositoryNotFoundException e )
236 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
238 catch ( RepositoryException e )
240 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
246 this.networkProxyMap.clear();
248 List<NetworkProxyConfiguration> networkProxies = archivaConfiguration.getConfiguration().getNetworkProxies();
249 for ( NetworkProxyConfiguration networkProxyConfig : networkProxies )
251 String key = networkProxyConfig.getId();
253 ProxyInfo proxy = new ProxyInfo();
255 proxy.setType( networkProxyConfig.getProtocol() );
256 proxy.setHost( networkProxyConfig.getHost() );
257 proxy.setPort( networkProxyConfig.getPort() );
258 proxy.setUserName( networkProxyConfig.getUsername() );
259 proxy.setPassword( networkProxyConfig.getPassword() );
261 this.networkProxyMap.put( key, proxy );
266 public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
267 throws ProxyDownloadException
269 File localFile = toLocalFile( repository, artifact );
271 Properties requestProperties = new Properties();
272 requestProperties.setProperty( "filetype", "artifact" );
273 requestProperties.setProperty( "version", artifact.getVersion() );
274 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
276 List<ProxyConnector> connectors = getProxyConnectors( repository );
277 Map<String, Exception> previousExceptions = new LinkedHashMap<String, Exception>();
278 for ( ProxyConnector connector : connectors )
280 if ( connector.isDisabled() )
285 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
286 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
288 String targetPath = targetRepository.toPath( artifact );
290 if ( SystemUtils.IS_OS_WINDOWS )
292 // toPath use system PATH_SEPARATOR so on windows url are \ which doesn't work very well :-)
293 targetPath = FilenameUtils.separatorsToUnix( targetPath );
298 File downloadedFile =
299 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
302 if ( fileExists( downloadedFile ) )
304 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
305 return downloadedFile;
308 catch ( NotFoundException e )
310 log.debug( "Artifact {} not found on repository \"{}\".", Keys.toKey( artifact ),
311 targetRepository.getRepository().getId() );
313 catch ( NotModifiedException e )
315 log.debug( "Artifact {} not updated on repository \"{}\".", Keys.toKey( artifact ),
316 targetRepository.getRepository().getId() );
318 catch ( ProxyException e )
320 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
321 targetRepository, localFile, e, previousExceptions );
323 catch ( RepositoryAdminException e )
325 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
326 targetRepository, localFile, e, previousExceptions );
330 if ( !previousExceptions.isEmpty() )
332 throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories",
333 previousExceptions );
336 log.debug( "Exhausted all target repositories, artifact {} not found.", Keys.toKey( artifact ) );
341 public File fetchFromProxies( ManagedRepositoryContent repository, String path )
343 File localFile = new File( repository.getRepoRoot(), path );
345 // no update policies for these paths
346 if ( localFile.exists() )
351 Properties requestProperties = new Properties();
352 requestProperties.setProperty( "filetype", "resource" );
353 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
355 List<ProxyConnector> connectors = getProxyConnectors( repository );
356 for ( ProxyConnector connector : connectors )
358 if ( connector.isDisabled() )
363 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
364 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
366 String targetPath = path;
370 File downloadedFile =
371 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
374 if ( fileExists( downloadedFile ) )
376 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
377 return downloadedFile;
380 catch ( NotFoundException e )
382 log.debug( "Resource {} not found on repository \"{}\".", path,
383 targetRepository.getRepository().getId() );
385 catch ( NotModifiedException e )
387 log.debug( "Resource {} not updated on repository \"{}\".", path,
388 targetRepository.getRepository().getId() );
390 catch ( ProxyException e )
393 "Transfer error from repository \"" + targetRepository.getRepository().getId() + "\" for resource "
394 + path + ", continuing to next repository. Error message: " + e.getMessage() );
395 log.debug( "Full stack trace", e );
397 catch ( RepositoryAdminException e )
400 "Transfer error from repository \"" + targetRepository.getRepository().getId() + "\" for resource "
401 + path + ", continuing to next repository. Error message: " + e.getMessage() );
402 log.debug( "Full stack trace", e );
406 log.debug( "Exhausted all target repositories, resource {} not found.", path );
411 public File fetchMetatadaFromProxies( ManagedRepositoryContent repository, String logicalPath )
413 File localFile = new File( repository.getRepoRoot(), logicalPath );
415 Properties requestProperties = new Properties();
416 requestProperties.setProperty( "filetype", "metadata" );
417 boolean metadataNeedsUpdating = false;
418 long originalTimestamp = getLastModified( localFile );
420 List<ProxyConnector> connectors = getProxyConnectors( repository );
421 for ( ProxyConnector connector : connectors )
423 if ( connector.isDisabled() )
428 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
430 File localRepoFile = toLocalRepoFile( repository, targetRepository, logicalPath );
431 long originalMetadataTimestamp = getLastModified( localRepoFile );
435 transferFile( connector, targetRepository, logicalPath, repository, localRepoFile, requestProperties,
438 if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
440 metadataNeedsUpdating = true;
443 catch ( NotFoundException e )
446 log.debug( "Metadata {} not found on remote repository '{}'.", logicalPath,
447 targetRepository.getRepository().getId(), e );
450 catch ( NotModifiedException e )
453 log.debug( "Metadata {} not updated on remote repository '{}'.", logicalPath,
454 targetRepository.getRepository().getId(), e );
457 catch ( ProxyException e )
459 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId()
460 + "\" for versioned Metadata " + logicalPath
461 + ", continuing to next repository. Error message: " + e.getMessage() );
462 log.debug( "Full stack trace", e );
464 catch ( RepositoryAdminException e )
466 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId()
467 + "\" for versioned Metadata " + logicalPath
468 + ", continuing to next repository. Error message: " + e.getMessage() );
469 log.debug( "Full stack trace", e );
473 if ( hasBeenUpdated( localFile, originalTimestamp ) )
475 metadataNeedsUpdating = true;
478 if ( metadataNeedsUpdating || !localFile.exists() )
482 metadataTools.updateMetadata( repository, logicalPath );
484 catch ( RepositoryMetadataException e )
486 log.warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
490 if ( fileExists( localFile ) )
498 private long getLastModified( File file )
500 if ( !file.exists() || !file.isFile() )
505 return file.lastModified();
508 private boolean hasBeenUpdated( File file, long originalLastModified )
510 if ( !file.exists() || !file.isFile() )
515 long currentLastModified = getLastModified( file );
516 return ( currentLastModified > originalLastModified );
519 private File toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository,
522 String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath );
523 return new File( repository.getRepoRoot(), repoPath );
527 * Test if the provided ManagedRepositoryContent has any proxies configured for it.
529 public boolean hasProxies( ManagedRepositoryContent repository )
531 synchronized ( this.proxyConnectorMap )
533 return this.proxyConnectorMap.containsKey( repository.getId() );
537 private File toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact )
539 return repository.toFile( artifact );
543 * Simple method to test if the file exists on the local disk.
545 * @param file the file to test. (may be null)
546 * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File.
548 private boolean fileExists( File file )
555 if ( !file.exists() )
560 if ( !file.isFile() )
569 * Perform the transfer of the file.
571 * @param connector the connector configuration to use.
572 * @param remoteRepository the remote repository get the resource from.
573 * @param remotePath the path in the remote repository to the resource to get.
574 * @param repository the managed repository that will hold the file
575 * @param resource the local file to place the downloaded resource into
576 * @param requestProperties the request properties to utilize for policy handling.
577 * @param executeConsumers whether to execute the consumers after proxying
578 * @return the local file that was downloaded, or null if not downloaded.
579 * @throws NotFoundException if the file was not found on the remote repository.
580 * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository, but
581 * the remote resource is not newer than the local File.
582 * @throws ProxyException if transfer was unsuccessful.
584 private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath,
585 ManagedRepositoryContent repository, File resource, Properties requestProperties,
586 boolean executeConsumers )
587 throws ProxyException, NotModifiedException, RepositoryAdminException
589 String url = remoteRepository.getURL().getUrl();
590 if ( !url.endsWith( "/" ) )
594 url = url + remotePath;
595 requestProperties.setProperty( "url", url );
597 // Is a whitelist defined?
598 if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
600 // Path must belong to whitelist.
601 if ( !matchesPattern( remotePath, connector.getWhitelist() ) )
603 log.debug( "Path [{}] is not part of defined whitelist (skipping transfer from repository [{}]).",
604 remotePath, remoteRepository.getRepository().getName() );
609 // Is target path part of blacklist?
610 if ( matchesPattern( remotePath, connector.getBlacklist() ) )
612 log.debug( "Path [{}] is part of blacklist (skipping transfer from repository [{}]).", remotePath,
613 remoteRepository.getRepository().getName() );
617 // Handle pre-download policy
620 validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, resource );
622 catch ( PolicyViolationException e )
624 String emsg = "Transfer not attempted on " + url + " : " + e.getMessage();
625 if ( fileExists( resource ) )
627 log.debug( "{} : using already present local file.", emsg );
637 File tmpResource = null;
639 File workingDirectory = createWorkingDirectory( repository );
645 RepositoryURL repoUrl = remoteRepository.getURL();
646 String protocol = repoUrl.getProtocol();
647 NetworkProxy networkProxy = null;
648 if ( StringUtils.isNotBlank( connector.getProxyId() ) )
650 networkProxy = networkProxyAdmin.getNetworkProxy( connector.getProxyId() );
653 wagon = ( networkProxy != null && networkProxy.isUseNtlm() ) ? wagonFactory.getWagon(
654 "wagon#" + protocol + "-ntlm" ) : wagonFactory.getWagon( "wagon#" + protocol );
657 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
660 boolean connected = connectToRepository( connector, wagon, remoteRepository );
663 tmpResource = new File( workingDirectory, resource.getName() );
664 transferSimpleFile( wagon, remoteRepository, remotePath, repository, resource, tmpResource );
666 // TODO: these should be used to validate the download based on the policies, not always downloaded
668 // save on connections since md5 is rarely used
670 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
673 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
677 catch ( NotFoundException e )
679 urlFailureCache.cacheFailure( url );
682 catch ( NotModifiedException e )
684 // Do not cache url here.
687 catch ( ProxyException e )
689 urlFailureCache.cacheFailure( url );
692 catch ( WagonFactoryException e )
694 throw new ProxyException( e.getMessage(), e );
704 catch ( ConnectionException e )
706 log.warn( "Unable to disconnect wagon.", e );
711 // Handle post-download policies.
714 validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, tmpResource );
716 catch ( PolicyViolationException e )
718 log.warn( "Transfer invalidated from {} : {}", url, e.getMessage() );
719 executeConsumers = false;
720 if ( !fileExists( tmpResource ) )
726 if ( resource != null )
728 synchronized ( resource.getAbsolutePath().intern() )
730 File directory = resource.getParentFile();
731 moveFileIfExists( tmpMd5, directory );
732 moveFileIfExists( tmpSha1, directory );
733 moveFileIfExists( tmpResource, directory );
739 FileUtils.deleteQuietly( workingDirectory );
742 if ( executeConsumers )
744 // Just-in-time update of the index and database by executing the consumers for this artifact
745 //consumers.executeConsumers( connector.getSourceRepository().getRepository(), resource );
746 queueRepositoryTask( connector.getSourceRepository().getRepository().getId(), resource );
752 private void queueRepositoryTask( String repositoryId, File localFile )
754 RepositoryTask task = new RepositoryTask();
755 task.setRepositoryId( repositoryId );
756 task.setResourceFile( localFile );
757 task.setUpdateRelatedArtifacts( true );
758 task.setScanAll( true );
762 scheduler.queueTask( task );
764 catch ( TaskQueueException e )
766 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
772 * Moves the file into repository location if it exists
774 * @param fileToMove this could be either the main artifact, sha1 or md5 checksum file.
775 * @param directory directory to write files to
777 private void moveFileIfExists( File fileToMove, File directory )
778 throws ProxyException
780 if ( fileToMove != null && fileToMove.exists() )
782 File newLocation = new File( directory, fileToMove.getName() );
783 moveTempToTarget( fileToMove, newLocation );
789 * Quietly transfer the checksum file from the remote repository to the local file.
792 * @param wagon the wagon instance (should already be connected) to use.
793 * @param remoteRepository the remote repository to transfer from.
794 * @param remotePath the remote path to the resource to get.
795 * @param repository the managed repository that will hold the file
796 * @param resource the local file that should contain the downloaded contents
797 * @param tmpDirectory the temporary directory to download to
798 * @param ext the type of checksum to transfer (example: ".md5" or ".sha1")
799 * @throws ProxyException if copying the downloaded file into place did not succeed.
801 private File transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
802 ManagedRepositoryContent repository, File resource, File tmpDirectory, String ext )
803 throws ProxyException
805 String url = remoteRepository.getURL().getUrl() + remotePath + ext;
807 // Transfer checksum does not use the policy.
808 if ( urlFailureCache.hasFailedBefore( url ) )
813 File destFile = new File( tmpDirectory, resource.getName() + ext );
817 transferSimpleFile( wagon, remoteRepository, remotePath + ext, repository, resource, destFile );
818 log.debug( "Checksum {} Downloaded: {} to move to {}", Arrays.asList( url, destFile, resource ).toArray() );
820 catch ( NotFoundException e )
822 urlFailureCache.cacheFailure( url );
823 log.debug( "Transfer failed, checksum not found: {}", url );
824 // Consume it, do not pass this on.
826 catch ( NotModifiedException e )
828 log.debug( "Transfer skipped, checksum not modified: {}", url );
829 // Consume it, do not pass this on.
831 catch ( ProxyException e )
833 urlFailureCache.cacheFailure( url );
834 log.warn( "Transfer failed on checksum: " + url + " : " + e.getMessage(), e );
835 // Critical issue, pass it on.
842 * Perform the transfer of the remote file to the local file specified.
844 * @param wagon the wagon instance to use.
845 * @param remoteRepository the remote repository to use
846 * @param remotePath the remote path to attempt to get
847 * @param repository the managed repository that will hold the file
848 * @param origFile the local file to save to
849 * @return The local file that was transfered.
850 * @throws ProxyException if there was a problem moving the downloaded file into place.
851 * @throws WagonException if there was a problem tranfering the file.
853 private void transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
854 ManagedRepositoryContent repository, File origFile, File destFile )
855 throws ProxyException
857 assert ( remotePath != null );
859 // Transfer the file.
862 boolean success = false;
864 if ( !origFile.exists() )
866 log.debug( "Retrieving {} from {}", remotePath, remoteRepository.getRepository().getName() );
867 wagon.get( remotePath, destFile );
870 // You wouldn't get here on failure, a WagonException would have been thrown.
871 log.debug( "Downloaded successfully." );
875 log.debug( "Retrieving {} from {} if updated", remotePath, remoteRepository.getRepository().getName() );
876 success = wagon.getIfNewer( remotePath, destFile, origFile.lastModified() );
879 throw new NotModifiedException(
880 "Not downloaded, as local file is newer than remote side: " + origFile.getAbsolutePath() );
883 if ( destFile.exists() )
885 log.debug( "Downloaded successfully." );
889 catch ( ResourceDoesNotExistException e )
891 throw new NotFoundException(
892 "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(),
895 catch ( WagonException e )
897 // TODO: shouldn't have to drill into the cause, but TransferFailedException is often not descriptive enough
900 "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage();
901 if ( e.getCause() != null )
903 msg += " (cause: " + e.getCause() + ")";
905 throw new ProxyException( msg, e );
910 * Apply the policies.
912 * @param policies the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
913 * @param settings the map of settings for the policies to execute. (Map of String policy keys, to String policy
915 * @param request the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, File)}
917 * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, File)})
919 private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings,
920 Properties request, File localFile )
921 throws PolicyViolationException
923 for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
925 // olamy with spring rolehint is now downloadPolicy#hint
926 // so substring after last # to get the hint as with plexus
927 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
928 DownloadPolicy policy = entry.getValue();
929 String defaultSetting = policy.getDefaultOption();
931 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
933 log.debug( "Applying [{}] policy with [{}]", key, setting );
936 policy.applyPolicy( setting, request, localFile );
938 catch ( PolicyConfigurationException e )
940 log.error( e.getMessage(), e );
945 private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings,
946 Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
947 File localFile, Exception exception, Map<String, Exception> previousExceptions )
948 throws ProxyDownloadException
950 boolean process = true;
951 for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
954 // olamy with spring rolehint is now downloadPolicy#hint
955 // so substring after last # to get the hint as with plexus
956 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
957 DownloadErrorPolicy policy = entry.getValue();
958 String defaultSetting = policy.getDefaultOption();
959 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
961 log.debug( "Applying [{}] policy with [{}]", key, setting );
964 // all policies must approve the exception, any can cancel
965 process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions );
971 catch ( PolicyConfigurationException e )
973 log.error( e.getMessage(), e );
979 // if the exception was queued, don't throw it
980 if ( !previousExceptions.containsKey( content.getId() ) )
982 throw new ProxyDownloadException(
983 "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
984 content.getId(), exception );
989 // if the exception was queued, but cancelled, remove it
990 previousExceptions.remove( content.getId() );
994 "Transfer error from repository \"" + content.getRepository().getId() + "\" for artifact " + Keys.toKey(
995 artifact ) + ", continuing to next repository. Error message: " + exception.getMessage() );
996 log.debug( "Full stack trace", exception );
1000 * Creates a working directory
1003 * @return file location of working directory
1004 * @throws IOException
1006 private File createWorkingDirectory( ManagedRepositoryContent repository )
1008 return Files.createTempDir();
1012 * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles its
1015 * @param temp The completed download file
1016 * @param target The final location of the downloaded file
1017 * @throws ProxyException when the temp file cannot replace the target file
1019 private void moveTempToTarget( File temp, File target )
1020 throws ProxyException
1022 if ( target.exists() && !target.delete() )
1024 throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() );
1027 target.getParentFile().mkdirs();
1028 if ( !temp.renameTo( target ) )
1030 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
1034 FileUtils.copyFile( temp, target );
1036 catch ( IOException e )
1038 if ( target.exists() )
1040 log.debug( "Tried to copy file {} to {} but file with this name already exists.", temp.getName(),
1041 target.getAbsolutePath() );
1045 throw new ProxyException(
1046 "Cannot copy tmp file " + temp.getAbsolutePath() + " to its final location", e );
1051 FileUtils.deleteQuietly( temp );
1057 * Using wagon, connect to the remote repository.
1059 * @param connector the connector configuration to utilize (for obtaining network proxy configuration from)
1060 * @param wagon the wagon instance to establish the connection on.
1061 * @param remoteRepository the remote repository to connect to.
1062 * @return true if the connection was successful. false if not connected.
1064 private boolean connectToRepository( ProxyConnector connector, Wagon wagon,
1065 RemoteRepositoryContent remoteRepository )
1067 boolean connected = false;
1069 final ProxyInfo networkProxy =
1070 connector.getProxyId() == null ? null : this.networkProxyMap.get( connector.getProxyId() );
1072 if ( log.isDebugEnabled() )
1074 if ( networkProxy != null )
1076 // TODO: move to proxyInfo.toString()
1077 String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
1078 + " to connect to remote repository " + remoteRepository.getURL();
1079 if ( networkProxy.getNonProxyHosts() != null )
1081 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
1083 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
1085 msg += "; as user: " + networkProxy.getUserName();
1091 AuthenticationInfo authInfo = null;
1092 String username = remoteRepository.getRepository().getUserName();
1093 String password = remoteRepository.getRepository().getPassword();
1095 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
1097 log.debug( "Using username {} to connect to remote repository {}", username, remoteRepository.getURL() );
1098 authInfo = new AuthenticationInfo();
1099 authInfo.setUserName( username );
1100 authInfo.setPassword( password );
1103 // Convert seconds to milliseconds
1104 int timeoutInMilliseconds = remoteRepository.getRepository().getTimeout() * 1000;
1106 // Set timeout read and connect
1107 // FIXME olamy having 2 config values
1108 wagon.setReadTimeout( timeoutInMilliseconds );
1109 wagon.setTimeout( timeoutInMilliseconds );
1113 Repository wagonRepository =
1114 new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
1115 wagon.connect( wagonRepository, authInfo, networkProxy );
1118 catch ( ConnectionException e )
1120 log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1123 catch ( AuthenticationException e )
1125 log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1133 * Tests whitelist and blacklist patterns against path.
1135 * @param path the path to test.
1136 * @param patterns the list of patterns to check.
1137 * @return true if the path matches at least 1 pattern in the provided patterns list.
1139 private boolean matchesPattern( String path, List<String> patterns )
1141 if ( CollectionUtils.isEmpty( patterns ) )
1146 if ( !path.startsWith( "/" ) )
1151 for ( String pattern : patterns )
1153 if ( !pattern.startsWith( "/" ) )
1155 pattern = "/" + pattern;
1158 if ( SelectorUtils.matchPath( pattern, path, false ) )
1168 * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
1170 public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository )
1172 synchronized ( this.proxyConnectorMap )
1174 List<ProxyConnector> ret = (List<ProxyConnector>) this.proxyConnectorMap.get( repository.getId() );
1177 return Collections.emptyList();
1180 Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
1185 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1187 if ( ConfigurationNames.isNetworkProxy( propertyName ) || ConfigurationNames.isManagedRepositories(
1188 propertyName ) || ConfigurationNames.isRemoteRepositories( propertyName )
1189 || ConfigurationNames.isProxyConnector( propertyName ) )
1191 initConnectorsAndNetworkProxies();
1195 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1200 public ArchivaConfiguration getArchivaConfiguration()
1202 return archivaConfiguration;
1205 public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
1207 this.archivaConfiguration = archivaConfiguration;
1210 public RepositoryContentFactory getRepositoryFactory()
1212 return repositoryFactory;
1215 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
1217 this.repositoryFactory = repositoryFactory;
1220 public MetadataTools getMetadataTools()
1222 return metadataTools;
1225 public void setMetadataTools( MetadataTools metadataTools )
1227 this.metadataTools = metadataTools;
1230 public UrlFailureCache getUrlFailureCache()
1232 return urlFailureCache;
1235 public void setUrlFailureCache( UrlFailureCache urlFailureCache )
1237 this.urlFailureCache = urlFailureCache;
1240 public WagonFactory getWagonFactory()
1242 return wagonFactory;
1245 public void setWagonFactory( WagonFactory wagonFactory )
1247 this.wagonFactory = wagonFactory;
1250 public Map<String, PreDownloadPolicy> getPreDownloadPolicies()
1252 return preDownloadPolicies;
1255 public void setPreDownloadPolicies( Map<String, PreDownloadPolicy> preDownloadPolicies )
1257 this.preDownloadPolicies = preDownloadPolicies;
1260 public Map<String, PostDownloadPolicy> getPostDownloadPolicies()
1262 return postDownloadPolicies;
1265 public void setPostDownloadPolicies( Map<String, PostDownloadPolicy> postDownloadPolicies )
1267 this.postDownloadPolicies = postDownloadPolicies;
1270 public Map<String, DownloadErrorPolicy> getDownloadErrorPolicies()
1272 return downloadErrorPolicies;
1275 public void setDownloadErrorPolicies( Map<String, DownloadErrorPolicy> downloadErrorPolicies )
1277 this.downloadErrorPolicies = downloadErrorPolicies;