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 com.google.common.io.Files;
24 import org.apache.archiva.admin.model.RepositoryAdminException;
25 import org.apache.archiva.admin.model.beans.NetworkProxy;
26 import org.apache.archiva.admin.model.networkproxy.NetworkProxyAdmin;
27 import org.apache.archiva.configuration.ArchivaConfiguration;
28 import org.apache.archiva.configuration.ConfigurationNames;
29 import org.apache.archiva.configuration.NetworkProxyConfiguration;
30 import org.apache.archiva.configuration.ProxyConnectorConfiguration;
31 import org.apache.archiva.model.ArtifactReference;
32 import org.apache.archiva.model.Keys;
33 import org.apache.archiva.model.RepositoryURL;
34 import org.apache.archiva.policies.DownloadErrorPolicy;
35 import org.apache.archiva.policies.DownloadPolicy;
36 import org.apache.archiva.policies.PolicyConfigurationException;
37 import org.apache.archiva.policies.PolicyViolationException;
38 import org.apache.archiva.policies.PostDownloadPolicy;
39 import org.apache.archiva.policies.PreDownloadPolicy;
40 import org.apache.archiva.policies.ProxyDownloadException;
41 import org.apache.archiva.policies.urlcache.UrlFailureCache;
42 import org.apache.archiva.proxy.common.WagonFactory;
43 import org.apache.archiva.proxy.common.WagonFactoryException;
44 import org.apache.archiva.repository.ManagedRepositoryContent;
45 import org.apache.archiva.repository.RemoteRepositoryContent;
46 import org.apache.archiva.repository.RepositoryContentFactory;
47 import org.apache.archiva.repository.RepositoryException;
48 import org.apache.archiva.repository.RepositoryNotFoundException;
49 import org.apache.archiva.repository.metadata.MetadataTools;
50 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
51 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
52 import org.apache.archiva.scheduler.repository.RepositoryTask;
53 import org.apache.commons.collections.CollectionUtils;
54 import org.apache.commons.io.FileUtils;
55 import org.apache.commons.io.FilenameUtils;
56 import org.apache.commons.lang.StringUtils;
57 import org.apache.commons.lang.SystemUtils;
58 import org.apache.maven.wagon.ConnectionException;
59 import org.apache.maven.wagon.ResourceDoesNotExistException;
60 import org.apache.maven.wagon.Wagon;
61 import org.apache.maven.wagon.WagonException;
62 import org.apache.maven.wagon.authentication.AuthenticationException;
63 import org.apache.maven.wagon.authentication.AuthenticationInfo;
64 import org.apache.maven.wagon.proxy.ProxyInfo;
65 import org.apache.maven.wagon.repository.Repository;
66 import org.codehaus.plexus.registry.Registry;
67 import org.codehaus.plexus.registry.RegistryListener;
68 import org.codehaus.plexus.taskqueue.TaskQueueException;
69 import org.codehaus.plexus.util.SelectorUtils;
70 import org.slf4j.Logger;
71 import org.slf4j.LoggerFactory;
72 import org.springframework.stereotype.Service;
74 import javax.annotation.PostConstruct;
75 import javax.inject.Inject;
76 import javax.inject.Named;
78 import java.io.IOException;
79 import java.util.ArrayList;
80 import java.util.Arrays;
81 import java.util.Collections;
82 import java.util.HashMap;
83 import java.util.LinkedHashMap;
84 import java.util.List;
86 import java.util.Map.Entry;
87 import java.util.Properties;
88 import java.util.concurrent.ConcurrentHashMap;
91 * DefaultRepositoryProxyConnectors
94 * @todo exception handling needs work - "not modified" is not really an exceptional case, and it has more layers than
95 * your average brown onion
97 @Service( "repositoryProxyConnectors#default" )
98 public class DefaultRepositoryProxyConnectors
99 implements RepositoryProxyConnectors, RegistryListener
101 private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class );
107 @Named( value = "archivaConfiguration#default" )
108 private ArchivaConfiguration archivaConfiguration;
114 @Named( value = "repositoryContentFactory#default" )
115 private RepositoryContentFactory repositoryFactory;
121 @Named( value = "metadataTools#default" )
122 private MetadataTools metadataTools;
128 private Map<String, PreDownloadPolicy> preDownloadPolicies;
134 private Map<String, PostDownloadPolicy> postDownloadPolicies;
140 private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
146 private UrlFailureCache urlFailureCache;
148 private Map<String, List<ProxyConnector>> proxyConnectorMap = new HashMap<String, List<ProxyConnector>>();
150 private Map<String, ProxyInfo> networkProxyMap = new ConcurrentHashMap<String, ProxyInfo>();
156 private WagonFactory wagonFactory;
162 @Named( value = "archivaTaskScheduler#repository" )
163 private ArchivaTaskScheduler scheduler;
166 private NetworkProxyAdmin networkProxyAdmin;
169 public void initialize()
171 initConnectorsAndNetworkProxies();
172 archivaConfiguration.addChangeListener( this );
176 @SuppressWarnings( "unchecked" )
177 private void initConnectorsAndNetworkProxies()
180 ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator();
181 this.proxyConnectorMap.clear();
183 List<ProxyConnectorConfiguration> proxyConfigs = archivaConfiguration.getConfiguration().getProxyConnectors();
184 for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs )
186 String key = proxyConfig.getSourceRepoId();
190 // Create connector object.
191 ProxyConnector connector = new ProxyConnector();
193 connector.setSourceRepository(
194 repositoryFactory.getManagedRepositoryContent( proxyConfig.getSourceRepoId() ) );
195 connector.setTargetRepository(
196 repositoryFactory.getRemoteRepositoryContent( proxyConfig.getTargetRepoId() ) );
198 connector.setProxyId( proxyConfig.getProxyId() );
199 connector.setPolicies( proxyConfig.getPolicies() );
200 connector.setOrder( proxyConfig.getOrder() );
201 connector.setDisabled( proxyConfig.isDisabled() );
203 // Copy any blacklist patterns.
204 List<String> blacklist = new ArrayList<String>( 0 );
205 if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) )
207 blacklist.addAll( proxyConfig.getBlackListPatterns() );
209 connector.setBlacklist( blacklist );
211 // Copy any whitelist patterns.
212 List<String> whitelist = new ArrayList<String>( 0 );
213 if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) )
215 whitelist.addAll( proxyConfig.getWhiteListPatterns() );
217 connector.setWhitelist( whitelist );
219 // Get other connectors
220 List<ProxyConnector> connectors = this.proxyConnectorMap.get( key );
221 if ( connectors == null )
223 // Create if we are the first.
224 connectors = new ArrayList<ProxyConnector>( 1 );
227 // Add the connector.
228 connectors.add( connector );
230 // Ensure the list is sorted.
231 Collections.sort( connectors, proxyOrderSorter );
233 // Set the key to the list of connectors.
234 this.proxyConnectorMap.put( key, connectors );
236 catch ( RepositoryNotFoundException e )
238 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
240 catch ( RepositoryException e )
242 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
248 this.networkProxyMap.clear();
250 List<NetworkProxyConfiguration> networkProxies = archivaConfiguration.getConfiguration().getNetworkProxies();
251 for ( NetworkProxyConfiguration networkProxyConfig : networkProxies )
253 String key = networkProxyConfig.getId();
255 ProxyInfo proxy = new ProxyInfo();
257 proxy.setType( networkProxyConfig.getProtocol() );
258 proxy.setHost( networkProxyConfig.getHost() );
259 proxy.setPort( networkProxyConfig.getPort() );
260 proxy.setUserName( networkProxyConfig.getUsername() );
261 proxy.setPassword( networkProxyConfig.getPassword() );
263 this.networkProxyMap.put( key, proxy );
268 public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
269 throws ProxyDownloadException
271 File localFile = toLocalFile( repository, artifact );
273 Properties requestProperties = new Properties();
274 requestProperties.setProperty( "filetype", "artifact" );
275 requestProperties.setProperty( "version", artifact.getVersion() );
276 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
278 List<ProxyConnector> connectors = getProxyConnectors( repository );
279 Map<String, Exception> previousExceptions = new LinkedHashMap<String, Exception>();
280 for ( ProxyConnector connector : connectors )
282 if ( connector.isDisabled() )
287 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
288 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
290 String targetPath = targetRepository.toPath( artifact );
292 if ( SystemUtils.IS_OS_WINDOWS )
294 // toPath use system PATH_SEPARATOR so on windows url are \ which doesn't work very well :-)
295 targetPath = FilenameUtils.separatorsToUnix( targetPath );
300 File downloadedFile =
301 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
304 if ( fileExists( downloadedFile ) )
306 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
307 return downloadedFile;
310 catch ( NotFoundException e )
312 log.debug( "Artifact {} not found on repository \"{}\".", Keys.toKey( artifact ),
313 targetRepository.getRepository().getId() );
315 catch ( NotModifiedException e )
317 log.debug( "Artifact {} not updated on repository \"{}\".", Keys.toKey( artifact ),
318 targetRepository.getRepository().getId() );
320 catch ( ProxyException e )
322 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
323 targetRepository, localFile, e, previousExceptions );
325 catch ( RepositoryAdminException e )
327 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
328 targetRepository, localFile, e, previousExceptions );
332 if ( !previousExceptions.isEmpty() )
334 throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories",
335 previousExceptions );
338 log.debug( "Exhausted all target repositories, artifact {} not found.", Keys.toKey( artifact ) );
343 public File fetchFromProxies( ManagedRepositoryContent repository, String path )
345 File localFile = new File( repository.getRepoRoot(), path );
347 // no update policies for these paths
348 if ( localFile.exists() )
353 Properties requestProperties = new Properties();
354 requestProperties.setProperty( "filetype", "resource" );
355 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
357 List<ProxyConnector> connectors = getProxyConnectors( repository );
358 for ( ProxyConnector connector : connectors )
360 if ( connector.isDisabled() )
365 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
366 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
368 String targetPath = path;
372 File downloadedFile =
373 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
376 if ( fileExists( downloadedFile ) )
378 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
379 return downloadedFile;
382 catch ( NotFoundException e )
384 log.debug( "Resource {} not found on repository \"{}\".", path,
385 targetRepository.getRepository().getId() );
387 catch ( NotModifiedException e )
389 log.debug( "Resource {} not updated on repository \"{}\".", path,
390 targetRepository.getRepository().getId() );
392 catch ( ProxyException e )
395 "Transfer error from repository \"" + targetRepository.getRepository().getId() + "\" for resource "
396 + path + ", continuing to next repository. Error message: " + e.getMessage() );
397 log.debug( "Full stack trace", e );
399 catch ( RepositoryAdminException e )
402 "Transfer error from repository \"" + targetRepository.getRepository().getId() + "\" for resource "
403 + path + ", continuing to next repository. Error message: " + e.getMessage() );
404 log.debug( "Full stack trace", e );
408 log.debug( "Exhausted all target repositories, resource {} not found.", path );
413 public File fetchMetatadaFromProxies( ManagedRepositoryContent repository, String logicalPath )
415 File localFile = new File( repository.getRepoRoot(), logicalPath );
417 Properties requestProperties = new Properties();
418 requestProperties.setProperty( "filetype", "metadata" );
419 boolean metadataNeedsUpdating = false;
420 long originalTimestamp = getLastModified( localFile );
422 List<ProxyConnector> connectors = getProxyConnectors( repository );
423 for ( ProxyConnector connector : connectors )
425 if ( connector.isDisabled() )
430 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
432 File localRepoFile = toLocalRepoFile( repository, targetRepository, logicalPath );
433 long originalMetadataTimestamp = getLastModified( localRepoFile );
437 transferFile( connector, targetRepository, logicalPath, repository, localRepoFile, requestProperties,
440 if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
442 metadataNeedsUpdating = true;
445 catch ( NotFoundException e )
447 if ( log.isDebugEnabled() )
449 log.debug( "Metadata {} not found on remote repository \"{}\".",
450 Lists.<Object>newArrayList( logicalPath, targetRepository.getRepository().getId() ), e );
453 catch ( NotModifiedException e )
455 if ( log.isDebugEnabled() )
457 log.debug( "Metadata {} not updated on remote repository \"{}\".",
458 Lists.<Object>newArrayList( logicalPath, targetRepository.getRepository().getId() ), e );
461 catch ( ProxyException e )
463 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId()
464 + "\" for versioned Metadata " + logicalPath
465 + ", continuing to next repository. Error message: " + e.getMessage() );
466 log.debug( "Full stack trace", e );
468 catch ( RepositoryAdminException e )
470 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId()
471 + "\" for versioned Metadata " + logicalPath
472 + ", continuing to next repository. Error message: " + e.getMessage() );
473 log.debug( "Full stack trace", e );
477 if ( hasBeenUpdated( localFile, originalTimestamp ) )
479 metadataNeedsUpdating = true;
482 if ( metadataNeedsUpdating || !localFile.exists() )
486 metadataTools.updateMetadata( repository, logicalPath );
488 catch ( RepositoryMetadataException e )
490 log.warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
494 if ( fileExists( localFile ) )
502 private long getLastModified( File file )
504 if ( !file.exists() || !file.isFile() )
509 return file.lastModified();
512 private boolean hasBeenUpdated( File file, long originalLastModified )
514 if ( !file.exists() || !file.isFile() )
519 long currentLastModified = getLastModified( file );
520 return ( currentLastModified > originalLastModified );
523 private File toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository,
526 String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath );
527 return new File( repository.getRepoRoot(), repoPath );
531 * Test if the provided ManagedRepositoryContent has any proxies configured for it.
533 public boolean hasProxies( ManagedRepositoryContent repository )
535 synchronized ( this.proxyConnectorMap )
537 return this.proxyConnectorMap.containsKey( repository.getId() );
541 private File toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact )
543 return repository.toFile( artifact );
547 * Simple method to test if the file exists on the local disk.
549 * @param file the file to test. (may be null)
550 * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File.
552 private boolean fileExists( File file )
559 if ( !file.exists() )
564 if ( !file.isFile() )
573 * Perform the transfer of the file.
575 * @param connector the connector configuration to use.
576 * @param remoteRepository the remote repository get the resource from.
577 * @param remotePath the path in the remote repository to the resource to get.
578 * @param repository the managed repository that will hold the file
579 * @param resource the local file to place the downloaded resource into
580 * @param requestProperties the request properties to utilize for policy handling.
581 * @param executeConsumers whether to execute the consumers after proxying
582 * @return the local file that was downloaded, or null if not downloaded.
583 * @throws NotFoundException if the file was not found on the remote repository.
584 * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository, but
585 * the remote resource is not newer than the local File.
586 * @throws ProxyException if transfer was unsuccessful.
588 private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath,
589 ManagedRepositoryContent repository, File resource, Properties requestProperties,
590 boolean executeConsumers )
591 throws ProxyException, NotModifiedException, RepositoryAdminException
593 String url = remoteRepository.getURL().getUrl();
594 if ( !url.endsWith( "/" ) )
598 url = url + remotePath;
599 requestProperties.setProperty( "url", url );
601 // Is a whitelist defined?
602 if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
604 // Path must belong to whitelist.
605 if ( !matchesPattern( remotePath, connector.getWhitelist() ) )
607 log.debug( "Path [{}] is not part of defined whitelist (skipping transfer from repository [{}]).",
608 remotePath, remoteRepository.getRepository().getName() );
613 // Is target path part of blacklist?
614 if ( matchesPattern( remotePath, connector.getBlacklist() ) )
616 log.debug( "Path [{}] is part of blacklist (skipping transfer from repository [{}]).", remotePath,
617 remoteRepository.getRepository().getName() );
621 // Handle pre-download policy
624 validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, resource );
626 catch ( PolicyViolationException e )
628 String emsg = "Transfer not attempted on " + url + " : " + e.getMessage();
629 if ( fileExists( resource ) )
631 log.debug( "{} : using already present local file.", emsg );
641 File tmpResource = null;
643 File workingDirectory = createWorkingDirectory( repository );
649 RepositoryURL repoUrl = remoteRepository.getURL();
650 String protocol = repoUrl.getProtocol();
651 NetworkProxy networkProxy = null;
652 if ( StringUtils.isNotBlank( connector.getProxyId() ) )
654 networkProxy = networkProxyAdmin.getNetworkProxy( connector.getProxyId() );
657 wagon = ( networkProxy != null && networkProxy.isUseNtlm() ) ? wagonFactory.getWagon(
658 "wagon#" + protocol + "-ntlm" ) : wagonFactory.getWagon( "wagon#" + protocol );
661 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
664 boolean connected = connectToRepository( connector, wagon, remoteRepository );
667 tmpResource = new File( workingDirectory, resource.getName() );
668 transferSimpleFile( wagon, remoteRepository, remotePath, repository, resource, tmpResource );
670 // TODO: these should be used to validate the download based on the policies, not always downloaded
672 // save on connections since md5 is rarely used
674 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
677 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
681 catch ( NotFoundException e )
683 urlFailureCache.cacheFailure( url );
686 catch ( NotModifiedException e )
688 // Do not cache url here.
691 catch ( ProxyException e )
693 urlFailureCache.cacheFailure( url );
696 catch ( WagonFactoryException e )
698 throw new ProxyException( e.getMessage(), e );
708 catch ( ConnectionException e )
710 log.warn( "Unable to disconnect wagon.", e );
715 // Handle post-download policies.
718 validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, tmpResource );
720 catch ( PolicyViolationException e )
722 log.warn( "Transfer invalidated from {} : {}", url, e.getMessage() );
723 executeConsumers = false;
724 if ( !fileExists( tmpResource ) )
730 if ( resource != null )
732 synchronized ( resource.getAbsolutePath().intern() )
734 File directory = resource.getParentFile();
735 moveFileIfExists( tmpMd5, directory );
736 moveFileIfExists( tmpSha1, directory );
737 moveFileIfExists( tmpResource, directory );
743 FileUtils.deleteQuietly( workingDirectory );
746 if ( executeConsumers )
748 // Just-in-time update of the index and database by executing the consumers for this artifact
749 //consumers.executeConsumers( connector.getSourceRepository().getRepository(), resource );
750 queueRepositoryTask( connector.getSourceRepository().getRepository().getId(), resource );
756 private void queueRepositoryTask( String repositoryId, File localFile )
758 RepositoryTask task = new RepositoryTask();
759 task.setRepositoryId( repositoryId );
760 task.setResourceFile( localFile );
761 task.setUpdateRelatedArtifacts( true );
762 task.setScanAll( true );
766 scheduler.queueTask( task );
768 catch ( TaskQueueException e )
770 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
776 * Moves the file into repository location if it exists
778 * @param fileToMove this could be either the main artifact, sha1 or md5 checksum file.
779 * @param directory directory to write files to
781 private void moveFileIfExists( File fileToMove, File directory )
782 throws ProxyException
784 if ( fileToMove != null && fileToMove.exists() )
786 File newLocation = new File( directory, fileToMove.getName() );
787 moveTempToTarget( fileToMove, newLocation );
793 * Quietly transfer the checksum file from the remote repository to the local file.
796 * @param wagon the wagon instance (should already be connected) to use.
797 * @param remoteRepository the remote repository to transfer from.
798 * @param remotePath the remote path to the resource to get.
799 * @param repository the managed repository that will hold the file
800 * @param resource the local file that should contain the downloaded contents
801 * @param tmpDirectory the temporary directory to download to
802 * @param ext the type of checksum to transfer (example: ".md5" or ".sha1")
803 * @throws ProxyException if copying the downloaded file into place did not succeed.
805 private File transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
806 ManagedRepositoryContent repository, File resource, File tmpDirectory, String ext )
807 throws ProxyException
809 String url = remoteRepository.getURL().getUrl() + remotePath + ext;
811 // Transfer checksum does not use the policy.
812 if ( urlFailureCache.hasFailedBefore( url ) )
817 File destFile = new File( tmpDirectory, resource.getName() + ext );
821 transferSimpleFile( wagon, remoteRepository, remotePath + ext, repository, resource, destFile );
822 log.debug( "Checksum {} Downloaded: {} to move to {}", Arrays.asList( url, destFile, resource ).toArray() );
824 catch ( NotFoundException e )
826 urlFailureCache.cacheFailure( url );
827 log.debug( "Transfer failed, checksum not found: {}", url );
828 // Consume it, do not pass this on.
830 catch ( NotModifiedException e )
832 log.debug( "Transfer skipped, checksum not modified: {}", url );
833 // Consume it, do not pass this on.
835 catch ( ProxyException e )
837 urlFailureCache.cacheFailure( url );
838 log.warn( "Transfer failed on checksum: " + url + " : " + e.getMessage(), e );
839 // Critical issue, pass it on.
846 * Perform the transfer of the remote file to the local file specified.
848 * @param wagon the wagon instance to use.
849 * @param remoteRepository the remote repository to use
850 * @param remotePath the remote path to attempt to get
851 * @param repository the managed repository that will hold the file
852 * @param origFile the local file to save to
853 * @return The local file that was transfered.
854 * @throws ProxyException if there was a problem moving the downloaded file into place.
855 * @throws WagonException if there was a problem tranfering the file.
857 private void transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
858 ManagedRepositoryContent repository, File origFile, File destFile )
859 throws ProxyException
861 assert ( remotePath != null );
863 // Transfer the file.
866 boolean success = false;
868 if ( !origFile.exists() )
870 log.debug( "Retrieving {} from {}", remotePath, remoteRepository.getRepository().getName() );
871 wagon.get( remotePath, destFile );
874 // You wouldn't get here on failure, a WagonException would have been thrown.
875 log.debug( "Downloaded successfully." );
879 log.debug( "Retrieving {} from {} if updated", remotePath, remoteRepository.getRepository().getName() );
880 success = wagon.getIfNewer( remotePath, destFile, origFile.lastModified() );
883 throw new NotModifiedException(
884 "Not downloaded, as local file is newer than remote side: " + origFile.getAbsolutePath() );
887 if ( destFile.exists() )
889 log.debug( "Downloaded successfully." );
893 catch ( ResourceDoesNotExistException e )
895 throw new NotFoundException(
896 "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(),
899 catch ( WagonException e )
901 // TODO: shouldn't have to drill into the cause, but TransferFailedException is often not descriptive enough
904 "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage();
905 if ( e.getCause() != null )
907 msg += " (cause: " + e.getCause() + ")";
909 throw new ProxyException( msg, e );
914 * Apply the policies.
916 * @param policies the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
917 * @param settings the map of settings for the policies to execute. (Map of String policy keys, to String policy
919 * @param request the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, File)}
921 * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, File)})
923 private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings,
924 Properties request, File localFile )
925 throws PolicyViolationException
927 for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
929 // olamy with spring rolehint is now downloadPolicy#hint
930 // so substring after last # to get the hint as with plexus
931 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
932 DownloadPolicy policy = entry.getValue();
933 String defaultSetting = policy.getDefaultOption();
935 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
937 log.debug( "Applying [{}] policy with [{}]", key, setting );
940 policy.applyPolicy( setting, request, localFile );
942 catch ( PolicyConfigurationException e )
944 log.error( e.getMessage(), e );
949 private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings,
950 Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
951 File localFile, Exception exception, Map<String, Exception> previousExceptions )
952 throws ProxyDownloadException
954 boolean process = true;
955 for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
958 // olamy with spring rolehint is now downloadPolicy#hint
959 // so substring after last # to get the hint as with plexus
960 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
961 DownloadErrorPolicy policy = entry.getValue();
962 String defaultSetting = policy.getDefaultOption();
963 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
965 log.debug( "Applying [{}] policy with [{}]", key, setting );
968 // all policies must approve the exception, any can cancel
969 process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions );
975 catch ( PolicyConfigurationException e )
977 log.error( e.getMessage(), e );
983 // if the exception was queued, don't throw it
984 if ( !previousExceptions.containsKey( content.getId() ) )
986 throw new ProxyDownloadException(
987 "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
988 content.getId(), exception );
993 // if the exception was queued, but cancelled, remove it
994 previousExceptions.remove( content.getId() );
998 "Transfer error from repository \"" + content.getRepository().getId() + "\" for artifact " + Keys.toKey(
999 artifact ) + ", continuing to next repository. Error message: " + exception.getMessage() );
1000 log.debug( "Full stack trace", exception );
1004 * Creates a working directory
1007 * @return file location of working directory
1008 * @throws IOException
1010 private File createWorkingDirectory( ManagedRepositoryContent repository )
1012 // TODO: This is ugly - lets actually clean this up when we get the new repository api
1016 File tmpDir = File.createTempFile( ".workingdirectory", null );
1021 catch ( IOException e )
1023 throw new RuntimeException( "Could not create working directory for this request", e );
1025 return Files.createTempDir();
1029 * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles its
1032 * @param temp The completed download file
1033 * @param target The final location of the downloaded file
1034 * @throws ProxyException when the temp file cannot replace the target file
1036 private void moveTempToTarget( File temp, File target )
1037 throws ProxyException
1039 if ( target.exists() && !target.delete() )
1041 throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() );
1044 target.getParentFile().mkdirs();
1045 if ( !temp.renameTo( target ) )
1047 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
1051 FileUtils.copyFile( temp, target );
1053 catch ( IOException e )
1055 if ( target.exists() )
1057 log.debug( "Tried to copy file {} to {} but file with this name already exists.", temp.getName(),
1058 target.getAbsolutePath() );
1062 throw new ProxyException(
1063 "Cannot copy tmp file " + temp.getAbsolutePath() + " to its final location", e );
1068 FileUtils.deleteQuietly( temp );
1074 * Using wagon, connect to the remote repository.
1076 * @param connector the connector configuration to utilize (for obtaining network proxy configuration from)
1077 * @param wagon the wagon instance to establish the connection on.
1078 * @param remoteRepository the remote repository to connect to.
1079 * @return true if the connection was successful. false if not connected.
1081 private boolean connectToRepository( ProxyConnector connector, Wagon wagon,
1082 RemoteRepositoryContent remoteRepository )
1084 boolean connected = false;
1086 final ProxyInfo networkProxy =
1087 connector.getProxyId() == null ? null : this.networkProxyMap.get( connector.getProxyId() );
1089 if ( log.isDebugEnabled() )
1091 if ( networkProxy != null )
1093 // TODO: move to proxyInfo.toString()
1094 String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
1095 + " to connect to remote repository " + remoteRepository.getURL();
1096 if ( networkProxy.getNonProxyHosts() != null )
1098 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
1100 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
1102 msg += "; as user: " + networkProxy.getUserName();
1108 AuthenticationInfo authInfo = null;
1109 String username = remoteRepository.getRepository().getUserName();
1110 String password = remoteRepository.getRepository().getPassword();
1112 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
1114 log.debug( "Using username {} to connect to remote repository {}", username, remoteRepository.getURL() );
1115 authInfo = new AuthenticationInfo();
1116 authInfo.setUserName( username );
1117 authInfo.setPassword( password );
1120 // Convert seconds to milliseconds
1121 int timeoutInMilliseconds = remoteRepository.getRepository().getTimeout() * 1000;
1123 // Set timeout read and connect
1124 // FIXME olamy having 2 config values
1125 wagon.setReadTimeout( timeoutInMilliseconds );
1126 wagon.setTimeout( timeoutInMilliseconds );
1130 Repository wagonRepository =
1131 new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
1132 wagon.connect( wagonRepository, authInfo, networkProxy );
1135 catch ( ConnectionException e )
1137 log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1140 catch ( AuthenticationException e )
1142 log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1150 * Tests whitelist and blacklist patterns against path.
1152 * @param path the path to test.
1153 * @param patterns the list of patterns to check.
1154 * @return true if the path matches at least 1 pattern in the provided patterns list.
1156 private boolean matchesPattern( String path, List<String> patterns )
1158 if ( CollectionUtils.isEmpty( patterns ) )
1163 if ( !path.startsWith( "/" ) )
1168 for ( String pattern : patterns )
1170 if ( !pattern.startsWith( "/" ) )
1172 pattern = "/" + pattern;
1175 if ( SelectorUtils.matchPath( pattern, path, false ) )
1185 * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
1187 public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository )
1189 synchronized ( this.proxyConnectorMap )
1191 List<ProxyConnector> ret = (List<ProxyConnector>) this.proxyConnectorMap.get( repository.getId() );
1194 return Collections.emptyList();
1197 Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
1202 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1204 if ( ConfigurationNames.isNetworkProxy( propertyName ) || ConfigurationNames.isManagedRepositories(
1205 propertyName ) || ConfigurationNames.isRemoteRepositories( propertyName )
1206 || ConfigurationNames.isProxyConnector( propertyName ) )
1208 initConnectorsAndNetworkProxies();
1212 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1217 public ArchivaConfiguration getArchivaConfiguration()
1219 return archivaConfiguration;
1222 public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
1224 this.archivaConfiguration = archivaConfiguration;
1227 public RepositoryContentFactory getRepositoryFactory()
1229 return repositoryFactory;
1232 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
1234 this.repositoryFactory = repositoryFactory;
1237 public MetadataTools getMetadataTools()
1239 return metadataTools;
1242 public void setMetadataTools( MetadataTools metadataTools )
1244 this.metadataTools = metadataTools;
1247 public UrlFailureCache getUrlFailureCache()
1249 return urlFailureCache;
1252 public void setUrlFailureCache( UrlFailureCache urlFailureCache )
1254 this.urlFailureCache = urlFailureCache;
1257 public WagonFactory getWagonFactory()
1259 return wagonFactory;
1262 public void setWagonFactory( WagonFactory wagonFactory )
1264 this.wagonFactory = wagonFactory;
1267 public Map<String, PreDownloadPolicy> getPreDownloadPolicies()
1269 return preDownloadPolicies;
1272 public void setPreDownloadPolicies( Map<String, PreDownloadPolicy> preDownloadPolicies )
1274 this.preDownloadPolicies = preDownloadPolicies;
1277 public Map<String, PostDownloadPolicy> getPostDownloadPolicies()
1279 return postDownloadPolicies;
1282 public void setPostDownloadPolicies( Map<String, PostDownloadPolicy> postDownloadPolicies )
1284 this.postDownloadPolicies = postDownloadPolicies;
1287 public Map<String, DownloadErrorPolicy> getDownloadErrorPolicies()
1289 return downloadErrorPolicies;
1292 public void setDownloadErrorPolicies( Map<String, DownloadErrorPolicy> downloadErrorPolicies )
1294 this.downloadErrorPolicies = downloadErrorPolicies;