1 package org.apache.maven.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 org.apache.archiva.scheduler.ArchivaTaskScheduler;
23 import org.apache.archiva.scheduler.repository.RepositoryTask;
24 import org.apache.commons.collections.CollectionUtils;
25 import org.apache.commons.io.FileUtils;
26 import org.apache.commons.lang.StringUtils;
27 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
28 import org.apache.maven.archiva.configuration.ConfigurationNames;
29 import org.apache.maven.archiva.configuration.NetworkProxyConfiguration;
30 import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration;
31 import org.apache.maven.archiva.model.ArtifactReference;
32 import org.apache.maven.archiva.model.Keys;
33 import org.apache.maven.archiva.model.RepositoryURL;
34 import org.apache.maven.archiva.policies.DownloadErrorPolicy;
35 import org.apache.maven.archiva.policies.DownloadPolicy;
36 import org.apache.maven.archiva.policies.PolicyConfigurationException;
37 import org.apache.maven.archiva.policies.PolicyViolationException;
38 import org.apache.maven.archiva.policies.PostDownloadPolicy;
39 import org.apache.maven.archiva.policies.PreDownloadPolicy;
40 import org.apache.maven.archiva.policies.ProxyDownloadException;
41 import org.apache.maven.archiva.policies.urlcache.UrlFailureCache;
42 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
43 import org.apache.maven.archiva.repository.RemoteRepositoryContent;
44 import org.apache.maven.archiva.repository.RepositoryContentFactory;
45 import org.apache.maven.archiva.repository.RepositoryException;
46 import org.apache.maven.archiva.repository.RepositoryNotFoundException;
47 import org.apache.maven.archiva.repository.metadata.MetadataTools;
48 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
49 import org.apache.maven.wagon.ConnectionException;
50 import org.apache.maven.wagon.ResourceDoesNotExistException;
51 import org.apache.maven.wagon.Wagon;
52 import org.apache.maven.wagon.WagonException;
53 import org.apache.maven.wagon.authentication.AuthenticationException;
54 import org.apache.maven.wagon.authentication.AuthenticationInfo;
55 import org.apache.maven.wagon.proxy.ProxyInfo;
56 import org.apache.maven.wagon.repository.Repository;
57 import org.codehaus.plexus.registry.Registry;
58 import org.codehaus.plexus.registry.RegistryListener;
59 import org.codehaus.plexus.taskqueue.TaskQueueException;
60 import org.codehaus.plexus.util.SelectorUtils;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63 import org.springframework.context.ApplicationContext;
64 import org.springframework.stereotype.Service;
66 import javax.annotation.PostConstruct;
67 import javax.inject.Inject;
68 import javax.inject.Named;
70 import java.io.IOException;
71 import java.util.ArrayList;
72 import java.util.Arrays;
73 import java.util.Collections;
74 import java.util.HashMap;
75 import java.util.LinkedHashMap;
76 import java.util.List;
78 import java.util.Map.Entry;
79 import java.util.Properties;
82 * DefaultRepositoryProxyConnectors
85 * @todo exception handling needs work - "not modified" is not really an exceptional case, and it has more layers than
86 * your average brown onion
87 * plexus.component role-hint="default"
89 @Service( "repositoryProxyConnectors#default" )
90 public class DefaultRepositoryProxyConnectors
91 implements RepositoryProxyConnectors, RegistryListener
93 private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class );
99 @Named( value = "archivaConfiguration#default" )
100 private ArchivaConfiguration archivaConfiguration;
106 @Named( value = "repositoryContentFactory#default" )
107 private RepositoryContentFactory repositoryFactory;
113 @Named( value = "metadataTools#default" )
114 private MetadataTools metadataTools;
117 private ApplicationContext applicationContext;
120 * plexus.requirement role="org.apache.maven.archiva.policies.PreDownloadPolicy"
122 private Map<String, PreDownloadPolicy> preDownloadPolicies;
125 * plexus.requirement role="org.apache.maven.archiva.policies.PostDownloadPolicy"
127 private Map<String, PostDownloadPolicy> postDownloadPolicies;
130 * plexus.requirement role="org.apache.maven.archiva.policies.DownloadErrorPolicy"
132 private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
135 * plexus.requirement role-hint="default"
138 private UrlFailureCache urlFailureCache;
140 private Map<String, List<ProxyConnector>> proxyConnectorMap = new HashMap<String, List<ProxyConnector>>();
142 private Map<String, ProxyInfo> networkProxyMap = new HashMap<String, ProxyInfo>();
148 private WagonFactory wagonFactory;
151 * plexus.requirement role="org.apache.archiva.scheduler.ArchivaTaskScheduler" role-hint="repository"
154 @Named( value = "archivaTaskScheduler#repository" )
155 private ArchivaTaskScheduler scheduler;
158 public void initialize()
160 initConnectorsAndNetworkProxies();
161 archivaConfiguration.addChangeListener( this );
162 this.postDownloadPolicies = applicationContext.getBeansOfType( PostDownloadPolicy.class );
163 this.preDownloadPolicies = applicationContext.getBeansOfType( PreDownloadPolicy.class );
164 this.downloadErrorPolicies = applicationContext.getBeansOfType( DownloadErrorPolicy.class );
167 @SuppressWarnings( "unchecked" )
168 private void initConnectorsAndNetworkProxies()
170 synchronized ( this.proxyConnectorMap )
172 ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator();
173 this.proxyConnectorMap.clear();
175 List<ProxyConnectorConfiguration> proxyConfigs =
176 archivaConfiguration.getConfiguration().getProxyConnectors();
177 for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs )
179 String key = proxyConfig.getSourceRepoId();
183 // Create connector object.
184 ProxyConnector connector = new ProxyConnector();
186 connector.setSourceRepository(
187 repositoryFactory.getManagedRepositoryContent( proxyConfig.getSourceRepoId() ) );
188 connector.setTargetRepository(
189 repositoryFactory.getRemoteRepositoryContent( proxyConfig.getTargetRepoId() ) );
191 connector.setProxyId( proxyConfig.getProxyId() );
192 connector.setPolicies( proxyConfig.getPolicies() );
193 connector.setOrder( proxyConfig.getOrder() );
194 connector.setDisabled( proxyConfig.isDisabled() );
196 // Copy any blacklist patterns.
197 List<String> blacklist = new ArrayList<String>();
198 if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) )
200 blacklist.addAll( proxyConfig.getBlackListPatterns() );
202 connector.setBlacklist( blacklist );
204 // Copy any whitelist patterns.
205 List<String> whitelist = new ArrayList<String>();
206 if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) )
208 whitelist.addAll( proxyConfig.getWhiteListPatterns() );
210 connector.setWhitelist( whitelist );
212 // Get other connectors
213 List<ProxyConnector> connectors = this.proxyConnectorMap.get( key );
214 if ( connectors == null )
216 // Create if we are the first.
217 connectors = new ArrayList<ProxyConnector>();
220 // Add the connector.
221 connectors.add( connector );
223 // Ensure the list is sorted.
224 Collections.sort( connectors, proxyOrderSorter );
226 // Set the key to the list of connectors.
227 this.proxyConnectorMap.put( key, connectors );
229 catch ( RepositoryNotFoundException e )
231 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
233 catch ( RepositoryException e )
235 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
241 synchronized ( this.networkProxyMap )
243 this.networkProxyMap.clear();
245 List<NetworkProxyConfiguration> networkProxies =
246 archivaConfiguration.getConfiguration().getNetworkProxies();
247 for ( NetworkProxyConfiguration networkProxyConfig : networkProxies )
249 String key = networkProxyConfig.getId();
251 ProxyInfo proxy = new ProxyInfo();
253 proxy.setType( networkProxyConfig.getProtocol() );
254 proxy.setHost( networkProxyConfig.getHost() );
255 proxy.setPort( networkProxyConfig.getPort() );
256 proxy.setUserName( networkProxyConfig.getUsername() );
257 proxy.setPassword( networkProxyConfig.getPassword() );
259 this.networkProxyMap.put( key, proxy );
264 public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
265 throws ProxyDownloadException
267 File localFile = toLocalFile( repository, artifact );
269 Properties requestProperties = new Properties();
270 requestProperties.setProperty( "filetype", "artifact" );
271 requestProperties.setProperty( "version", artifact.getVersion() );
272 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
274 List<ProxyConnector> connectors = getProxyConnectors( repository );
275 Map<String, Exception> previousExceptions = new LinkedHashMap<String, Exception>();
276 for ( ProxyConnector connector : connectors )
278 if ( connector.isDisabled() )
283 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
284 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
286 String targetPath = targetRepository.toPath( artifact );
290 File downloadedFile =
291 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
294 if ( fileExists( downloadedFile ) )
296 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
297 return downloadedFile;
300 catch ( NotFoundException e )
302 log.debug( "Artifact {} not found on repository \"{}\".", Keys.toKey( artifact ),
303 targetRepository.getRepository().getId() );
305 catch ( NotModifiedException e )
307 log.debug( "Artifact {} not updated on repository \"{}\".", Keys.toKey( artifact ),
308 targetRepository.getRepository().getId() );
310 catch ( ProxyException e )
312 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
313 targetRepository, localFile, e, previousExceptions );
317 if ( !previousExceptions.isEmpty() )
319 throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories",
320 previousExceptions );
323 log.debug( "Exhausted all target repositories, artifact {} not found.", Keys.toKey( artifact ) );
328 public File fetchFromProxies( ManagedRepositoryContent repository, String path )
330 File localFile = new File( repository.getRepoRoot(), path );
332 // no update policies for these paths
333 if ( localFile.exists() )
338 Properties requestProperties = new Properties();
339 requestProperties.setProperty( "filetype", "resource" );
340 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
342 List<ProxyConnector> connectors = getProxyConnectors( repository );
343 for ( ProxyConnector connector : connectors )
345 if ( connector.isDisabled() )
350 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
351 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
353 String targetPath = path;
357 File downloadedFile =
358 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
361 if ( fileExists( downloadedFile ) )
363 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
364 return downloadedFile;
367 catch ( NotFoundException e )
369 log.debug( "Resource {} not found on repository \"{}\".", path,
370 targetRepository.getRepository().getId() );
372 catch ( NotModifiedException e )
374 log.debug( "Resource {} not updated on repository \"{}\".", path,
375 targetRepository.getRepository().getId() );
377 catch ( ProxyException e )
380 "Transfer error from repository \"" + targetRepository.getRepository().getId() + "\" for resource "
381 + path + ", continuing to next repository. Error message: " + e.getMessage() );
382 log.debug( "Full stack trace", e );
386 log.debug( "Exhausted all target repositories, resource {} not found.", path );
391 public File fetchMetatadaFromProxies( ManagedRepositoryContent repository, String logicalPath )
393 File localFile = new File( repository.getRepoRoot(), logicalPath );
395 Properties requestProperties = new Properties();
396 requestProperties.setProperty( "filetype", "metadata" );
397 boolean metadataNeedsUpdating = false;
398 long originalTimestamp = getLastModified( localFile );
400 List<ProxyConnector> connectors = getProxyConnectors( repository );
401 for ( ProxyConnector connector : connectors )
403 if ( connector.isDisabled() )
408 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
410 File localRepoFile = toLocalRepoFile( repository, targetRepository, logicalPath );
411 long originalMetadataTimestamp = getLastModified( localRepoFile );
415 transferFile( connector, targetRepository, logicalPath, repository, localRepoFile, requestProperties,
418 if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
420 metadataNeedsUpdating = true;
423 catch ( NotFoundException e )
425 if ( log.isDebugEnabled() )
427 log.debug( "Metadata " + logicalPath + " not found on remote repository \""
428 + targetRepository.getRepository().getId() + "\".", e );
431 catch ( NotModifiedException e )
433 if ( log.isDebugEnabled() )
435 log.debug( "Metadata " + logicalPath + " not updated on remote repository \""
436 + targetRepository.getRepository().getId() + "\".", e );
439 catch ( ProxyException e )
441 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId()
442 + "\" for versioned Metadata " + logicalPath
443 + ", continuing to next repository. Error message: " + e.getMessage() );
444 log.debug( "Full stack trace", e );
448 if ( hasBeenUpdated( localFile, originalTimestamp ) )
450 metadataNeedsUpdating = true;
453 if ( metadataNeedsUpdating || !localFile.exists() )
457 metadataTools.updateMetadata( repository, logicalPath );
459 catch ( RepositoryMetadataException e )
461 log.warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
465 if ( fileExists( localFile ) )
473 private long getLastModified( File file )
475 if ( !file.exists() || !file.isFile() )
480 return file.lastModified();
483 private boolean hasBeenUpdated( File file, long originalLastModified )
485 if ( !file.exists() || !file.isFile() )
490 long currentLastModified = getLastModified( file );
491 return ( currentLastModified > originalLastModified );
494 private File toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository,
497 String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath );
498 return new File( repository.getRepoRoot(), repoPath );
502 * Test if the provided ManagedRepositoryContent has any proxies configured for it.
504 public boolean hasProxies( ManagedRepositoryContent repository )
506 synchronized ( this.proxyConnectorMap )
508 return this.proxyConnectorMap.containsKey( repository.getId() );
512 private File toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact )
514 return repository.toFile( artifact );
518 * Simple method to test if the file exists on the local disk.
520 * @param file the file to test. (may be null)
521 * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File.
523 private boolean fileExists( File file )
530 if ( !file.exists() )
535 if ( !file.isFile() )
544 * Perform the transfer of the file.
546 * @param connector the connector configuration to use.
547 * @param remoteRepository the remote repository get the resource from.
548 * @param remotePath the path in the remote repository to the resource to get.
549 * @param repository the managed repository that will hold the file
550 * @param resource the local file to place the downloaded resource into
551 * @param requestProperties the request properties to utilize for policy handling.
552 * @param executeConsumers whether to execute the consumers after proxying
553 * @return the local file that was downloaded, or null if not downloaded.
554 * @throws NotFoundException if the file was not found on the remote repository.
555 * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository, but
556 * the remote resource is not newer than the local File.
557 * @throws ProxyException if transfer was unsuccessful.
559 private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath,
560 ManagedRepositoryContent repository, File resource, Properties requestProperties,
561 boolean executeConsumers )
562 throws ProxyException, NotModifiedException
564 String url = remoteRepository.getURL().getUrl();
565 if ( !url.endsWith( "/" ) )
569 url = url + remotePath;
570 requestProperties.setProperty( "url", url );
572 // Is a whitelist defined?
573 if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
575 // Path must belong to whitelist.
576 if ( !matchesPattern( remotePath, connector.getWhitelist() ) )
578 log.debug( "Path [{}] is not part of defined whitelist (skipping transfer from repository [{}]).",
579 remotePath, remoteRepository.getRepository().getName() );
584 // Is target path part of blacklist?
585 if ( matchesPattern( remotePath, connector.getBlacklist() ) )
587 log.debug( "Path [{}] is part of blacklist (skipping transfer from repository [{}]).", remotePath,
588 remoteRepository.getRepository().getName() );
592 // Handle pre-download policy
595 validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, resource );
597 catch ( PolicyViolationException e )
599 String emsg = "Transfer not attempted on " + url + " : " + e.getMessage();
600 if ( fileExists( resource ) )
602 log.debug( "{} : using already present local file.", emsg );
612 File tmpResource = null;
614 File workingDirectory = createWorkingDirectory( repository );
620 RepositoryURL repoUrl = remoteRepository.getURL();
621 String protocol = repoUrl.getProtocol();
622 wagon = wagonFactory.getWagon( "wagon#" + protocol );
625 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
628 boolean connected = connectToRepository( connector, wagon, remoteRepository );
631 tmpResource = new File( workingDirectory, resource.getName() );
632 transferSimpleFile( wagon, remoteRepository, remotePath, repository, resource, tmpResource );
634 // TODO: these should be used to validate the download based on the policies, not always downloaded
636 // save on connections since md5 is rarely used
638 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
641 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
645 catch ( NotFoundException e )
647 urlFailureCache.cacheFailure( url );
650 catch ( NotModifiedException e )
652 // Do not cache url here.
655 catch ( ProxyException e )
657 urlFailureCache.cacheFailure( url );
660 catch ( WagonFactoryException e )
662 throw new ProxyException( e.getMessage(), e );
672 catch ( ConnectionException e )
674 log.warn( "Unable to disconnect wagon.", e );
679 // Handle post-download policies.
682 validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, tmpResource );
684 catch ( PolicyViolationException e )
686 log.warn( "Transfer invalidated from {} : {}", url, e.getMessage() );
687 executeConsumers = false;
688 if ( !fileExists( tmpResource ) )
694 if ( resource != null )
696 synchronized ( resource.getAbsolutePath().intern() )
698 File directory = resource.getParentFile();
699 moveFileIfExists( tmpMd5, directory );
700 moveFileIfExists( tmpSha1, directory );
701 moveFileIfExists( tmpResource, directory );
707 FileUtils.deleteQuietly( workingDirectory );
710 if ( executeConsumers )
712 // Just-in-time update of the index and database by executing the consumers for this artifact
713 //consumers.executeConsumers( connector.getSourceRepository().getRepository(), resource );
714 queueRepositoryTask( connector.getSourceRepository().getRepository().getId(), resource );
720 private void queueRepositoryTask( String repositoryId, File localFile )
722 RepositoryTask task = new RepositoryTask();
723 task.setRepositoryId( repositoryId );
724 task.setResourceFile( localFile );
725 task.setUpdateRelatedArtifacts( true );
726 task.setScanAll( true );
730 scheduler.queueTask( task );
732 catch ( TaskQueueException e )
734 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
740 * Moves the file into repository location if it exists
742 * @param fileToMove this could be either the main artifact, sha1 or md5 checksum file.
743 * @param directory directory to write files to
745 private void moveFileIfExists( File fileToMove, File directory )
746 throws ProxyException
748 if ( fileToMove != null && fileToMove.exists() )
750 File newLocation = new File( directory, fileToMove.getName() );
751 moveTempToTarget( fileToMove, newLocation );
757 * Quietly transfer the checksum file from the remote repository to the local file.
760 * @param wagon the wagon instance (should already be connected) to use.
761 * @param remoteRepository the remote repository to transfer from.
762 * @param remotePath the remote path to the resource to get.
763 * @param repository the managed repository that will hold the file
764 * @param resource the local file that should contain the downloaded contents
765 * @param tmpDirectory the temporary directory to download to
766 * @param ext the type of checksum to transfer (example: ".md5" or ".sha1")
767 * @throws ProxyException if copying the downloaded file into place did not succeed.
769 private File transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
770 ManagedRepositoryContent repository, File resource, File tmpDirectory, String ext )
771 throws ProxyException
773 String url = remoteRepository.getURL().getUrl() + remotePath + ext;
775 // Transfer checksum does not use the policy.
776 if ( urlFailureCache.hasFailedBefore( url ) )
781 File destFile = new File( tmpDirectory, resource.getName() + ext );
785 transferSimpleFile( wagon, remoteRepository, remotePath + ext, repository, resource, destFile );
786 log.debug( "Checksum {} Downloaded: {} to move to {}", Arrays.asList( url, destFile, resource ).toArray() );
788 catch ( NotFoundException e )
790 urlFailureCache.cacheFailure( url );
791 log.debug( "Transfer failed, checksum not found: {}", url );
792 // Consume it, do not pass this on.
794 catch ( NotModifiedException e )
796 log.debug( "Transfer skipped, checksum not modified: {}", url );
797 // Consume it, do not pass this on.
799 catch ( ProxyException e )
801 urlFailureCache.cacheFailure( url );
802 log.warn( "Transfer failed on checksum: " + url + " : " + e.getMessage(), e );
803 // Critical issue, pass it on.
810 * Perform the transfer of the remote file to the local file specified.
812 * @param wagon the wagon instance to use.
813 * @param remoteRepository the remote repository to use
814 * @param remotePath the remote path to attempt to get
815 * @param repository the managed repository that will hold the file
816 * @param origFile the local file to save to
817 * @return The local file that was transfered.
818 * @throws ProxyException if there was a problem moving the downloaded file into place.
819 * @throws WagonException if there was a problem tranfering the file.
821 private void transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
822 ManagedRepositoryContent repository, File origFile, File destFile )
823 throws ProxyException
825 assert ( remotePath != null );
827 // Transfer the file.
830 boolean success = false;
832 if ( !origFile.exists() )
834 log.debug( "Retrieving {} from {}", remotePath, remoteRepository.getRepository().getName() );
835 wagon.get( remotePath, destFile );
838 // You wouldn't get here on failure, a WagonException would have been thrown.
839 log.debug( "Downloaded successfully." );
843 log.debug( "Retrieving {} from {} if updated", remotePath, remoteRepository.getRepository().getName() );
844 success = wagon.getIfNewer( remotePath, destFile, origFile.lastModified() );
847 throw new NotModifiedException(
848 "Not downloaded, as local file is newer than remote side: " + origFile.getAbsolutePath() );
851 if ( destFile.exists() )
853 log.debug( "Downloaded successfully." );
857 catch ( ResourceDoesNotExistException e )
859 throw new NotFoundException(
860 "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(),
863 catch ( WagonException e )
865 // TODO: shouldn't have to drill into the cause, but TransferFailedException is often not descriptive enough
868 "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage();
869 if ( e.getCause() != null )
871 msg += " (cause: " + e.getCause() + ")";
873 throw new ProxyException( msg, e );
878 * Apply the policies.
880 * @param policies the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
881 * @param settings the map of settings for the policies to execute. (Map of String policy keys, to String policy
883 * @param request the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, File)}
885 * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, File)})
887 private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings,
888 Properties request, File localFile )
889 throws PolicyViolationException
891 for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
893 // olamy with spring rolehint is now downloadPolicy#hint
894 // so substring after last # to get the hint as with plexus
895 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
896 DownloadPolicy policy = entry.getValue();
897 String defaultSetting = policy.getDefaultOption();
899 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
901 log.debug( "Applying [{}] policy with [{}]", key, setting );
904 policy.applyPolicy( setting, request, localFile );
906 catch ( PolicyConfigurationException e )
908 log.error( e.getMessage(), e );
913 private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings,
914 Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
915 File localFile, ProxyException exception, Map<String, Exception> previousExceptions )
916 throws ProxyDownloadException
918 boolean process = true;
919 for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
922 // olamy with spring rolehint is now downloadPolicy#hint
923 // so substring after last # to get the hint as with plexus
924 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
925 DownloadErrorPolicy policy = entry.getValue();
926 String defaultSetting = policy.getDefaultOption();
927 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
929 log.debug( "Applying [" + key + "] policy with [" + setting + "]" );
932 // all policies must approve the exception, any can cancel
933 process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions );
939 catch ( PolicyConfigurationException e )
941 log.error( e.getMessage(), e );
947 // if the exception was queued, don't throw it
948 if ( !previousExceptions.containsKey( content.getId() ) )
950 throw new ProxyDownloadException(
951 "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
952 content.getId(), exception );
957 // if the exception was queued, but cancelled, remove it
958 previousExceptions.remove( content.getId() );
961 log.warn( "Transfer error from repository \"" + content.getRepository().getId() + "\" for artifact "
962 + Keys.toKey( artifact ) + ", continuing to next repository. Error message: "
963 + exception.getMessage() );
964 log.debug( "Full stack trace", exception );
968 * Creates a working directory in the repository root for this request
971 * @return file location of working directory
972 * @throws IOException
974 private File createWorkingDirectory( ManagedRepositoryContent repository )
976 // TODO: This is ugly - lets actually clean this up when we get the new repository api
979 File tmpDir = File.createTempFile( ".workingdirectory", null, new File( repository.getRepoRoot() ) );
984 catch ( IOException e )
986 throw new RuntimeException( "Could not create working directory for this request", e );
991 * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles its
994 * @param temp The completed download file
995 * @param target The final location of the downloaded file
996 * @throws ProxyException when the temp file cannot replace the target file
998 private void moveTempToTarget( File temp, File target )
999 throws ProxyException
1001 if ( target.exists() && !target.delete() )
1003 throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() );
1006 target.getParentFile().mkdirs();
1007 if ( !temp.renameTo( target ) )
1009 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
1013 FileUtils.copyFile( temp, target );
1015 catch ( IOException e )
1017 if ( target.exists() )
1019 log.debug( "Tried to copy file {} to {} but file with this name already exists.", temp.getName(),
1020 target.getAbsolutePath() );
1024 throw new ProxyException(
1025 "Cannot copy tmp file " + temp.getAbsolutePath() + " to its final location", e );
1030 FileUtils.deleteQuietly( temp );
1036 * Using wagon, connect to the remote repository.
1038 * @param connector the connector configuration to utilize (for obtaining network proxy configuration from)
1039 * @param wagon the wagon instance to establish the connection on.
1040 * @param remoteRepository the remote repository to connect to.
1041 * @return true if the connection was successful. false if not connected.
1043 private boolean connectToRepository( ProxyConnector connector, Wagon wagon,
1044 RemoteRepositoryContent remoteRepository )
1046 boolean connected = false;
1048 final ProxyInfo networkProxy;
1049 synchronized ( this.networkProxyMap )
1051 networkProxy = (ProxyInfo) this.networkProxyMap.get( connector.getProxyId() );
1054 if ( log.isDebugEnabled() )
1056 if ( networkProxy != null )
1058 // TODO: move to proxyInfo.toString()
1059 String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
1060 + " to connect to remote repository " + remoteRepository.getURL();
1061 if ( networkProxy.getNonProxyHosts() != null )
1063 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
1065 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
1067 msg += "; as user: " + networkProxy.getUserName();
1073 AuthenticationInfo authInfo = null;
1074 String username = remoteRepository.getRepository().getUsername();
1075 String password = remoteRepository.getRepository().getPassword();
1077 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
1079 log.debug( "Using username {} to connect to remote repository {}", username, remoteRepository.getURL() );
1080 authInfo = new AuthenticationInfo();
1081 authInfo.setUserName( username );
1082 authInfo.setPassword( password );
1085 // Convert seconds to milliseconds
1086 int timeoutInMilliseconds = remoteRepository.getRepository().getTimeout() * 1000;
1089 wagon.setTimeout( timeoutInMilliseconds );
1093 Repository wagonRepository =
1094 new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
1095 wagon.connect( wagonRepository, authInfo, networkProxy );
1098 catch ( ConnectionException e )
1100 log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1103 catch ( AuthenticationException e )
1105 log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1113 * Tests whitelist and blacklist patterns against path.
1115 * @param path the path to test.
1116 * @param patterns the list of patterns to check.
1117 * @return true if the path matches at least 1 pattern in the provided patterns list.
1119 private boolean matchesPattern( String path, List<String> patterns )
1121 if ( CollectionUtils.isEmpty( patterns ) )
1126 if ( !path.startsWith( "/" ) )
1131 for ( String pattern : patterns )
1133 if ( !pattern.startsWith( "/" ) )
1135 pattern = "/" + pattern;
1138 if ( SelectorUtils.matchPath( pattern, path, false ) )
1148 * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
1150 public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository )
1152 synchronized ( this.proxyConnectorMap )
1154 List<ProxyConnector> ret = (List<ProxyConnector>) this.proxyConnectorMap.get( repository.getId() );
1157 return Collections.emptyList();
1160 Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
1165 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1167 if ( ConfigurationNames.isNetworkProxy( propertyName )
1168 || ConfigurationNames.isManagedRepositories( propertyName )
1169 || ConfigurationNames.isRemoteRepositories( propertyName ) || ConfigurationNames.isProxyConnector(
1172 initConnectorsAndNetworkProxies();
1176 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1181 public ArchivaConfiguration getArchivaConfiguration()
1183 return archivaConfiguration;
1186 public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
1188 this.archivaConfiguration = archivaConfiguration;
1191 public RepositoryContentFactory getRepositoryFactory()
1193 return repositoryFactory;
1196 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
1198 this.repositoryFactory = repositoryFactory;
1201 public MetadataTools getMetadataTools()
1203 return metadataTools;
1206 public void setMetadataTools( MetadataTools metadataTools )
1208 this.metadataTools = metadataTools;
1211 public UrlFailureCache getUrlFailureCache()
1213 return urlFailureCache;
1216 public void setUrlFailureCache( UrlFailureCache urlFailureCache )
1218 this.urlFailureCache = urlFailureCache;
1221 public WagonFactory getWagonFactory()
1223 return wagonFactory;
1226 public void setWagonFactory( WagonFactory wagonFactory )
1228 this.wagonFactory = wagonFactory;
1231 public Map<String, PreDownloadPolicy> getPreDownloadPolicies()
1233 return preDownloadPolicies;
1236 public void setPreDownloadPolicies( Map<String, PreDownloadPolicy> preDownloadPolicies )
1238 this.preDownloadPolicies = preDownloadPolicies;
1241 public Map<String, PostDownloadPolicy> getPostDownloadPolicies()
1243 return postDownloadPolicies;
1246 public void setPostDownloadPolicies( Map<String, PostDownloadPolicy> postDownloadPolicies )
1248 this.postDownloadPolicies = postDownloadPolicies;
1251 public Map<String, DownloadErrorPolicy> getDownloadErrorPolicies()
1253 return downloadErrorPolicies;
1256 public void setDownloadErrorPolicies( Map<String, DownloadErrorPolicy> downloadErrorPolicies )
1258 this.downloadErrorPolicies = downloadErrorPolicies;