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 com.google.common.collect.Lists;
23 import org.apache.archiva.proxy.common.WagonFactory;
24 import org.apache.archiva.proxy.common.WagonFactoryException;
25 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
26 import org.apache.archiva.scheduler.repository.RepositoryTask;
27 import org.apache.commons.collections.CollectionUtils;
28 import org.apache.commons.io.FileUtils;
29 import org.apache.commons.lang.StringUtils;
30 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
31 import org.apache.maven.archiva.configuration.ConfigurationNames;
32 import org.apache.maven.archiva.configuration.NetworkProxyConfiguration;
33 import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration;
34 import org.apache.maven.archiva.model.ArtifactReference;
35 import org.apache.maven.archiva.model.Keys;
36 import org.apache.maven.archiva.model.RepositoryURL;
37 import org.apache.maven.archiva.policies.DownloadErrorPolicy;
38 import org.apache.maven.archiva.policies.DownloadPolicy;
39 import org.apache.maven.archiva.policies.PolicyConfigurationException;
40 import org.apache.maven.archiva.policies.PolicyViolationException;
41 import org.apache.maven.archiva.policies.PostDownloadPolicy;
42 import org.apache.maven.archiva.policies.PreDownloadPolicy;
43 import org.apache.maven.archiva.policies.ProxyDownloadException;
44 import org.apache.maven.archiva.policies.urlcache.UrlFailureCache;
45 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
46 import org.apache.maven.archiva.repository.RemoteRepositoryContent;
47 import org.apache.maven.archiva.repository.RepositoryContentFactory;
48 import org.apache.maven.archiva.repository.RepositoryException;
49 import org.apache.maven.archiva.repository.RepositoryNotFoundException;
50 import org.apache.maven.archiva.repository.metadata.MetadataTools;
51 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
52 import org.apache.maven.wagon.ConnectionException;
53 import org.apache.maven.wagon.ResourceDoesNotExistException;
54 import org.apache.maven.wagon.Wagon;
55 import org.apache.maven.wagon.WagonException;
56 import org.apache.maven.wagon.authentication.AuthenticationException;
57 import org.apache.maven.wagon.authentication.AuthenticationInfo;
58 import org.apache.maven.wagon.proxy.ProxyInfo;
59 import org.apache.maven.wagon.repository.Repository;
60 import org.codehaus.plexus.registry.Registry;
61 import org.codehaus.plexus.registry.RegistryListener;
62 import org.codehaus.plexus.taskqueue.TaskQueueException;
63 import org.codehaus.plexus.util.SelectorUtils;
64 import org.slf4j.Logger;
65 import org.slf4j.LoggerFactory;
66 import org.springframework.context.ApplicationContext;
67 import org.springframework.stereotype.Service;
69 import javax.annotation.PostConstruct;
70 import javax.inject.Inject;
71 import javax.inject.Named;
73 import java.io.IOException;
74 import java.util.ArrayList;
75 import java.util.Arrays;
76 import java.util.Collections;
77 import java.util.HashMap;
78 import java.util.LinkedHashMap;
79 import java.util.List;
81 import java.util.Map.Entry;
82 import java.util.Properties;
85 * DefaultRepositoryProxyConnectors
88 * @todo exception handling needs work - "not modified" is not really an exceptional case, and it has more layers than
89 * your average brown onion
91 @Service( "repositoryProxyConnectors#default" )
92 public class DefaultRepositoryProxyConnectors
93 implements RepositoryProxyConnectors, RegistryListener
95 private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class );
101 @Named( value = "archivaConfiguration#default" )
102 private ArchivaConfiguration archivaConfiguration;
108 @Named( value = "repositoryContentFactory#default" )
109 private RepositoryContentFactory repositoryFactory;
115 @Named( value = "metadataTools#default" )
116 private MetadataTools metadataTools;
119 //private ApplicationContext applicationContext;
122 * plexus.requirement role="org.apache.maven.archiva.policies.PreDownloadPolicy"
125 private Map<String, PreDownloadPolicy> preDownloadPolicies;
128 * plexus.requirement role="org.apache.maven.archiva.policies.PostDownloadPolicy"
131 private Map<String, PostDownloadPolicy> postDownloadPolicies;
134 * plexus.requirement role="org.apache.maven.archiva.policies.DownloadErrorPolicy"
137 private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
140 * plexus.requirement role-hint="default"
143 private UrlFailureCache urlFailureCache;
145 private Map<String, List<ProxyConnector>> proxyConnectorMap = new HashMap<String, List<ProxyConnector>>();
147 private Map<String, ProxyInfo> networkProxyMap = new HashMap<String, ProxyInfo>();
153 private WagonFactory wagonFactory;
156 * plexus.requirement role="org.apache.archiva.scheduler.ArchivaTaskScheduler" role-hint="repository"
159 @Named( value = "archivaTaskScheduler#repository" )
160 private ArchivaTaskScheduler scheduler;
163 public void initialize()
165 initConnectorsAndNetworkProxies();
166 archivaConfiguration.addChangeListener( this );
167 //this.postDownloadPolicies = applicationContext.getBeansOfType( PostDownloadPolicy.class );
168 //his.preDownloadPolicies = applicationContext.getBeansOfType( PreDownloadPolicy.class );
169 //this.downloadErrorPolicies = applicationContext.getBeansOfType( DownloadErrorPolicy.class );
172 @SuppressWarnings( "unchecked" )
173 private void initConnectorsAndNetworkProxies()
175 synchronized ( this.proxyConnectorMap )
177 ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator();
178 this.proxyConnectorMap.clear();
180 List<ProxyConnectorConfiguration> proxyConfigs =
181 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>();
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>();
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>();
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 synchronized ( this.networkProxyMap )
248 this.networkProxyMap.clear();
250 List<NetworkProxyConfiguration> networkProxies =
251 archivaConfiguration.getConfiguration().getNetworkProxies();
252 for ( NetworkProxyConfiguration networkProxyConfig : networkProxies )
254 String key = networkProxyConfig.getId();
256 ProxyInfo proxy = new ProxyInfo();
258 proxy.setType( networkProxyConfig.getProtocol() );
259 proxy.setHost( networkProxyConfig.getHost() );
260 proxy.setPort( networkProxyConfig.getPort() );
261 proxy.setUserName( networkProxyConfig.getUsername() );
262 proxy.setPassword( networkProxyConfig.getPassword() );
264 this.networkProxyMap.put( key, proxy );
269 public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
270 throws ProxyDownloadException
272 File localFile = toLocalFile( repository, artifact );
274 Properties requestProperties = new Properties();
275 requestProperties.setProperty( "filetype", "artifact" );
276 requestProperties.setProperty( "version", artifact.getVersion() );
277 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
279 List<ProxyConnector> connectors = getProxyConnectors( repository );
280 Map<String, Exception> previousExceptions = new LinkedHashMap<String, Exception>();
281 for ( ProxyConnector connector : connectors )
283 if ( connector.isDisabled() )
288 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
289 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
291 String targetPath = targetRepository.toPath( artifact );
295 File downloadedFile =
296 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
299 if ( fileExists( downloadedFile ) )
301 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
302 return downloadedFile;
305 catch ( NotFoundException e )
307 log.debug( "Artifact {} not found on repository \"{}\".", Keys.toKey( artifact ),
308 targetRepository.getRepository().getId() );
310 catch ( NotModifiedException e )
312 log.debug( "Artifact {} not updated on repository \"{}\".", Keys.toKey( artifact ),
313 targetRepository.getRepository().getId() );
315 catch ( ProxyException e )
317 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
318 targetRepository, localFile, e, previousExceptions );
322 if ( !previousExceptions.isEmpty() )
324 throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories",
325 previousExceptions );
328 log.debug( "Exhausted all target repositories, artifact {} not found.", Keys.toKey( artifact ) );
333 public File fetchFromProxies( ManagedRepositoryContent repository, String path )
335 File localFile = new File( repository.getRepoRoot(), path );
337 // no update policies for these paths
338 if ( localFile.exists() )
343 Properties requestProperties = new Properties();
344 requestProperties.setProperty( "filetype", "resource" );
345 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
347 List<ProxyConnector> connectors = getProxyConnectors( repository );
348 for ( ProxyConnector connector : connectors )
350 if ( connector.isDisabled() )
355 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
356 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
358 String targetPath = path;
362 File downloadedFile =
363 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
366 if ( fileExists( downloadedFile ) )
368 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
369 return downloadedFile;
372 catch ( NotFoundException e )
374 log.debug( "Resource {} not found on repository \"{}\".", path,
375 targetRepository.getRepository().getId() );
377 catch ( NotModifiedException e )
379 log.debug( "Resource {} not updated on repository \"{}\".", path,
380 targetRepository.getRepository().getId() );
382 catch ( ProxyException e )
385 "Transfer error from repository \"" + targetRepository.getRepository().getId() + "\" for resource "
386 + path + ", continuing to next repository. Error message: " + e.getMessage() );
387 log.debug( "Full stack trace", e );
391 log.debug( "Exhausted all target repositories, resource {} not found.", path );
396 public File fetchMetatadaFromProxies( ManagedRepositoryContent repository, String logicalPath )
398 File localFile = new File( repository.getRepoRoot(), logicalPath );
400 Properties requestProperties = new Properties();
401 requestProperties.setProperty( "filetype", "metadata" );
402 boolean metadataNeedsUpdating = false;
403 long originalTimestamp = getLastModified( localFile );
405 List<ProxyConnector> connectors = getProxyConnectors( repository );
406 for ( ProxyConnector connector : connectors )
408 if ( connector.isDisabled() )
413 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
415 File localRepoFile = toLocalRepoFile( repository, targetRepository, logicalPath );
416 long originalMetadataTimestamp = getLastModified( localRepoFile );
420 transferFile( connector, targetRepository, logicalPath, repository, localRepoFile, requestProperties,
423 if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
425 metadataNeedsUpdating = true;
428 catch ( NotFoundException e )
430 if ( log.isDebugEnabled() )
432 log.debug( "Metadata {} not found on remote repository \"{}\".",
433 Lists.<Object>newArrayList( logicalPath, targetRepository.getRepository().getId() ), e );
436 catch ( NotModifiedException e )
438 if ( log.isDebugEnabled() )
440 log.debug( "Metadata {} not updated on remote repository \"{}\".",
441 Lists.<Object>newArrayList( logicalPath, targetRepository.getRepository().getId() ), e );
444 catch ( ProxyException e )
446 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId()
447 + "\" for versioned Metadata " + logicalPath
448 + ", continuing to next repository. Error message: " + e.getMessage() );
449 log.debug( "Full stack trace", e );
453 if ( hasBeenUpdated( localFile, originalTimestamp ) )
455 metadataNeedsUpdating = true;
458 if ( metadataNeedsUpdating || !localFile.exists() )
462 metadataTools.updateMetadata( repository, logicalPath );
464 catch ( RepositoryMetadataException e )
466 log.warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
470 if ( fileExists( localFile ) )
478 private long getLastModified( File file )
480 if ( !file.exists() || !file.isFile() )
485 return file.lastModified();
488 private boolean hasBeenUpdated( File file, long originalLastModified )
490 if ( !file.exists() || !file.isFile() )
495 long currentLastModified = getLastModified( file );
496 return ( currentLastModified > originalLastModified );
499 private File toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository,
502 String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath );
503 return new File( repository.getRepoRoot(), repoPath );
507 * Test if the provided ManagedRepositoryContent has any proxies configured for it.
509 public boolean hasProxies( ManagedRepositoryContent repository )
511 synchronized ( this.proxyConnectorMap )
513 return this.proxyConnectorMap.containsKey( repository.getId() );
517 private File toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact )
519 return repository.toFile( artifact );
523 * Simple method to test if the file exists on the local disk.
525 * @param file the file to test. (may be null)
526 * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File.
528 private boolean fileExists( File file )
535 if ( !file.exists() )
540 if ( !file.isFile() )
549 * Perform the transfer of the file.
551 * @param connector the connector configuration to use.
552 * @param remoteRepository the remote repository get the resource from.
553 * @param remotePath the path in the remote repository to the resource to get.
554 * @param repository the managed repository that will hold the file
555 * @param resource the local file to place the downloaded resource into
556 * @param requestProperties the request properties to utilize for policy handling.
557 * @param executeConsumers whether to execute the consumers after proxying
558 * @return the local file that was downloaded, or null if not downloaded.
559 * @throws NotFoundException if the file was not found on the remote repository.
560 * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository, but
561 * the remote resource is not newer than the local File.
562 * @throws ProxyException if transfer was unsuccessful.
564 private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath,
565 ManagedRepositoryContent repository, File resource, Properties requestProperties,
566 boolean executeConsumers )
567 throws ProxyException, NotModifiedException
569 String url = remoteRepository.getURL().getUrl();
570 if ( !url.endsWith( "/" ) )
574 url = url + remotePath;
575 requestProperties.setProperty( "url", url );
577 // Is a whitelist defined?
578 if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
580 // Path must belong to whitelist.
581 if ( !matchesPattern( remotePath, connector.getWhitelist() ) )
583 log.debug( "Path [{}] is not part of defined whitelist (skipping transfer from repository [{}]).",
584 remotePath, remoteRepository.getRepository().getName() );
589 // Is target path part of blacklist?
590 if ( matchesPattern( remotePath, connector.getBlacklist() ) )
592 log.debug( "Path [{}] is part of blacklist (skipping transfer from repository [{}]).", remotePath,
593 remoteRepository.getRepository().getName() );
597 // Handle pre-download policy
600 validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, resource );
602 catch ( PolicyViolationException e )
604 String emsg = "Transfer not attempted on " + url + " : " + e.getMessage();
605 if ( fileExists( resource ) )
607 log.debug( "{} : using already present local file.", emsg );
617 File tmpResource = null;
619 File workingDirectory = createWorkingDirectory( repository );
625 RepositoryURL repoUrl = remoteRepository.getURL();
626 String protocol = repoUrl.getProtocol();
627 wagon = wagonFactory.getWagon( "wagon#" + protocol );
630 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
633 boolean connected = connectToRepository( connector, wagon, remoteRepository );
636 tmpResource = new File( workingDirectory, resource.getName() );
637 transferSimpleFile( wagon, remoteRepository, remotePath, repository, resource, tmpResource );
639 // TODO: these should be used to validate the download based on the policies, not always downloaded
641 // save on connections since md5 is rarely used
643 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
646 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
650 catch ( NotFoundException e )
652 urlFailureCache.cacheFailure( url );
655 catch ( NotModifiedException e )
657 // Do not cache url here.
660 catch ( ProxyException e )
662 urlFailureCache.cacheFailure( url );
665 catch ( WagonFactoryException e )
667 throw new ProxyException( e.getMessage(), e );
677 catch ( ConnectionException e )
679 log.warn( "Unable to disconnect wagon.", e );
684 // Handle post-download policies.
687 validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, tmpResource );
689 catch ( PolicyViolationException e )
691 log.warn( "Transfer invalidated from {} : {}", url, e.getMessage() );
692 executeConsumers = false;
693 if ( !fileExists( tmpResource ) )
699 if ( resource != null )
701 synchronized ( resource.getAbsolutePath().intern() )
703 File directory = resource.getParentFile();
704 moveFileIfExists( tmpMd5, directory );
705 moveFileIfExists( tmpSha1, directory );
706 moveFileIfExists( tmpResource, directory );
712 FileUtils.deleteQuietly( workingDirectory );
715 if ( executeConsumers )
717 // Just-in-time update of the index and database by executing the consumers for this artifact
718 //consumers.executeConsumers( connector.getSourceRepository().getRepository(), resource );
719 queueRepositoryTask( connector.getSourceRepository().getRepository().getId(), resource );
725 private void queueRepositoryTask( String repositoryId, File localFile )
727 RepositoryTask task = new RepositoryTask();
728 task.setRepositoryId( repositoryId );
729 task.setResourceFile( localFile );
730 task.setUpdateRelatedArtifacts( true );
731 task.setScanAll( true );
735 scheduler.queueTask( task );
737 catch ( TaskQueueException e )
739 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
745 * Moves the file into repository location if it exists
747 * @param fileToMove this could be either the main artifact, sha1 or md5 checksum file.
748 * @param directory directory to write files to
750 private void moveFileIfExists( File fileToMove, File directory )
751 throws ProxyException
753 if ( fileToMove != null && fileToMove.exists() )
755 File newLocation = new File( directory, fileToMove.getName() );
756 moveTempToTarget( fileToMove, newLocation );
762 * Quietly transfer the checksum file from the remote repository to the local file.
765 * @param wagon the wagon instance (should already be connected) to use.
766 * @param remoteRepository the remote repository to transfer from.
767 * @param remotePath the remote path to the resource to get.
768 * @param repository the managed repository that will hold the file
769 * @param resource the local file that should contain the downloaded contents
770 * @param tmpDirectory the temporary directory to download to
771 * @param ext the type of checksum to transfer (example: ".md5" or ".sha1")
772 * @throws ProxyException if copying the downloaded file into place did not succeed.
774 private File transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
775 ManagedRepositoryContent repository, File resource, File tmpDirectory, String ext )
776 throws ProxyException
778 String url = remoteRepository.getURL().getUrl() + remotePath + ext;
780 // Transfer checksum does not use the policy.
781 if ( urlFailureCache.hasFailedBefore( url ) )
786 File destFile = new File( tmpDirectory, resource.getName() + ext );
790 transferSimpleFile( wagon, remoteRepository, remotePath + ext, repository, resource, destFile );
791 log.debug( "Checksum {} Downloaded: {} to move to {}", Arrays.asList( url, destFile, resource ).toArray() );
793 catch ( NotFoundException e )
795 urlFailureCache.cacheFailure( url );
796 log.debug( "Transfer failed, checksum not found: {}", url );
797 // Consume it, do not pass this on.
799 catch ( NotModifiedException e )
801 log.debug( "Transfer skipped, checksum not modified: {}", url );
802 // Consume it, do not pass this on.
804 catch ( ProxyException e )
806 urlFailureCache.cacheFailure( url );
807 log.warn( "Transfer failed on checksum: " + url + " : " + e.getMessage(), e );
808 // Critical issue, pass it on.
815 * Perform the transfer of the remote file to the local file specified.
817 * @param wagon the wagon instance to use.
818 * @param remoteRepository the remote repository to use
819 * @param remotePath the remote path to attempt to get
820 * @param repository the managed repository that will hold the file
821 * @param origFile the local file to save to
822 * @return The local file that was transfered.
823 * @throws ProxyException if there was a problem moving the downloaded file into place.
824 * @throws WagonException if there was a problem tranfering the file.
826 private void transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
827 ManagedRepositoryContent repository, File origFile, File destFile )
828 throws ProxyException
830 assert ( remotePath != null );
832 // Transfer the file.
835 boolean success = false;
837 if ( !origFile.exists() )
839 log.debug( "Retrieving {} from {}", remotePath, remoteRepository.getRepository().getName() );
840 wagon.get( remotePath, destFile );
843 // You wouldn't get here on failure, a WagonException would have been thrown.
844 log.debug( "Downloaded successfully." );
848 log.debug( "Retrieving {} from {} if updated", remotePath, remoteRepository.getRepository().getName() );
849 success = wagon.getIfNewer( remotePath, destFile, origFile.lastModified() );
852 throw new NotModifiedException(
853 "Not downloaded, as local file is newer than remote side: " + origFile.getAbsolutePath() );
856 if ( destFile.exists() )
858 log.debug( "Downloaded successfully." );
862 catch ( ResourceDoesNotExistException e )
864 throw new NotFoundException(
865 "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(),
868 catch ( WagonException e )
870 // TODO: shouldn't have to drill into the cause, but TransferFailedException is often not descriptive enough
873 "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage();
874 if ( e.getCause() != null )
876 msg += " (cause: " + e.getCause() + ")";
878 throw new ProxyException( msg, e );
883 * Apply the policies.
885 * @param policies the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
886 * @param settings the map of settings for the policies to execute. (Map of String policy keys, to String policy
888 * @param request the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, File)}
890 * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, File)})
892 private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings,
893 Properties request, File localFile )
894 throws PolicyViolationException
896 for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
898 // olamy with spring rolehint is now downloadPolicy#hint
899 // so substring after last # to get the hint as with plexus
900 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
901 DownloadPolicy policy = entry.getValue();
902 String defaultSetting = policy.getDefaultOption();
904 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
906 log.debug( "Applying [{}] policy with [{}]", key, setting );
909 policy.applyPolicy( setting, request, localFile );
911 catch ( PolicyConfigurationException e )
913 log.error( e.getMessage(), e );
918 private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings,
919 Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
920 File localFile, ProxyException exception, Map<String, Exception> previousExceptions )
921 throws ProxyDownloadException
923 boolean process = true;
924 for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
927 // olamy with spring rolehint is now downloadPolicy#hint
928 // so substring after last # to get the hint as with plexus
929 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
930 DownloadErrorPolicy policy = entry.getValue();
931 String defaultSetting = policy.getDefaultOption();
932 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
934 log.debug( "Applying [{}] policy with [{}]", key, setting );
937 // all policies must approve the exception, any can cancel
938 process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions );
944 catch ( PolicyConfigurationException e )
946 log.error( e.getMessage(), e );
952 // if the exception was queued, don't throw it
953 if ( !previousExceptions.containsKey( content.getId() ) )
955 throw new ProxyDownloadException(
956 "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
957 content.getId(), exception );
962 // if the exception was queued, but cancelled, remove it
963 previousExceptions.remove( content.getId() );
966 log.warn( "Transfer error from repository \"" + content.getRepository().getId() + "\" for artifact "
967 + Keys.toKey( artifact ) + ", continuing to next repository. Error message: "
968 + exception.getMessage() );
969 log.debug( "Full stack trace", exception );
973 * Creates a working directory in the repository root for this request
976 * @return file location of working directory
977 * @throws IOException
979 private File createWorkingDirectory( ManagedRepositoryContent repository )
981 // TODO: This is ugly - lets actually clean this up when we get the new repository api
984 File tmpDir = File.createTempFile( ".workingdirectory", null, new File( repository.getRepoRoot() ) );
989 catch ( IOException e )
991 throw new RuntimeException( "Could not create working directory for this request", e );
996 * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles its
999 * @param temp The completed download file
1000 * @param target The final location of the downloaded file
1001 * @throws ProxyException when the temp file cannot replace the target file
1003 private void moveTempToTarget( File temp, File target )
1004 throws ProxyException
1006 if ( target.exists() && !target.delete() )
1008 throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() );
1011 target.getParentFile().mkdirs();
1012 if ( !temp.renameTo( target ) )
1014 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
1018 FileUtils.copyFile( temp, target );
1020 catch ( IOException e )
1022 if ( target.exists() )
1024 log.debug( "Tried to copy file {} to {} but file with this name already exists.", temp.getName(),
1025 target.getAbsolutePath() );
1029 throw new ProxyException(
1030 "Cannot copy tmp file " + temp.getAbsolutePath() + " to its final location", e );
1035 FileUtils.deleteQuietly( temp );
1041 * Using wagon, connect to the remote repository.
1043 * @param connector the connector configuration to utilize (for obtaining network proxy configuration from)
1044 * @param wagon the wagon instance to establish the connection on.
1045 * @param remoteRepository the remote repository to connect to.
1046 * @return true if the connection was successful. false if not connected.
1048 private boolean connectToRepository( ProxyConnector connector, Wagon wagon,
1049 RemoteRepositoryContent remoteRepository )
1051 boolean connected = false;
1053 final ProxyInfo networkProxy;
1054 synchronized ( this.networkProxyMap )
1056 networkProxy = (ProxyInfo) this.networkProxyMap.get( connector.getProxyId() );
1059 if ( log.isDebugEnabled() )
1061 if ( networkProxy != null )
1063 // TODO: move to proxyInfo.toString()
1064 String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
1065 + " to connect to remote repository " + remoteRepository.getURL();
1066 if ( networkProxy.getNonProxyHosts() != null )
1068 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
1070 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
1072 msg += "; as user: " + networkProxy.getUserName();
1078 AuthenticationInfo authInfo = null;
1079 String username = remoteRepository.getRepository().getUsername();
1080 String password = remoteRepository.getRepository().getPassword();
1082 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
1084 log.debug( "Using username {} to connect to remote repository {}", username, remoteRepository.getURL() );
1085 authInfo = new AuthenticationInfo();
1086 authInfo.setUserName( username );
1087 authInfo.setPassword( password );
1090 // Convert seconds to milliseconds
1091 int timeoutInMilliseconds = remoteRepository.getRepository().getTimeout() * 1000;
1094 wagon.setTimeout( timeoutInMilliseconds );
1098 Repository wagonRepository =
1099 new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
1100 wagon.connect( wagonRepository, authInfo, networkProxy );
1103 catch ( ConnectionException e )
1105 log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1108 catch ( AuthenticationException e )
1110 log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1118 * Tests whitelist and blacklist patterns against path.
1120 * @param path the path to test.
1121 * @param patterns the list of patterns to check.
1122 * @return true if the path matches at least 1 pattern in the provided patterns list.
1124 private boolean matchesPattern( String path, List<String> patterns )
1126 if ( CollectionUtils.isEmpty( patterns ) )
1131 if ( !path.startsWith( "/" ) )
1136 for ( String pattern : patterns )
1138 if ( !pattern.startsWith( "/" ) )
1140 pattern = "/" + pattern;
1143 if ( SelectorUtils.matchPath( pattern, path, false ) )
1153 * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
1155 public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository )
1157 synchronized ( this.proxyConnectorMap )
1159 List<ProxyConnector> ret = (List<ProxyConnector>) this.proxyConnectorMap.get( repository.getId() );
1162 return Collections.emptyList();
1165 Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
1170 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1172 if ( ConfigurationNames.isNetworkProxy( propertyName )
1173 || ConfigurationNames.isManagedRepositories( propertyName )
1174 || ConfigurationNames.isRemoteRepositories( propertyName ) || ConfigurationNames.isProxyConnector(
1177 initConnectorsAndNetworkProxies();
1181 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1186 public ArchivaConfiguration getArchivaConfiguration()
1188 return archivaConfiguration;
1191 public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
1193 this.archivaConfiguration = archivaConfiguration;
1196 public RepositoryContentFactory getRepositoryFactory()
1198 return repositoryFactory;
1201 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
1203 this.repositoryFactory = repositoryFactory;
1206 public MetadataTools getMetadataTools()
1208 return metadataTools;
1211 public void setMetadataTools( MetadataTools metadataTools )
1213 this.metadataTools = metadataTools;
1216 public UrlFailureCache getUrlFailureCache()
1218 return urlFailureCache;
1221 public void setUrlFailureCache( UrlFailureCache urlFailureCache )
1223 this.urlFailureCache = urlFailureCache;
1226 public WagonFactory getWagonFactory()
1228 return wagonFactory;
1231 public void setWagonFactory( WagonFactory wagonFactory )
1233 this.wagonFactory = wagonFactory;
1236 public Map<String, PreDownloadPolicy> getPreDownloadPolicies()
1238 return preDownloadPolicies;
1241 public void setPreDownloadPolicies( Map<String, PreDownloadPolicy> preDownloadPolicies )
1243 this.preDownloadPolicies = preDownloadPolicies;
1246 public Map<String, PostDownloadPolicy> getPostDownloadPolicies()
1248 return postDownloadPolicies;
1251 public void setPostDownloadPolicies( Map<String, PostDownloadPolicy> postDownloadPolicies )
1253 this.postDownloadPolicies = postDownloadPolicies;
1256 public Map<String, DownloadErrorPolicy> getDownloadErrorPolicies()
1258 return downloadErrorPolicies;
1261 public void setDownloadErrorPolicies( Map<String, DownloadErrorPolicy> downloadErrorPolicies )
1263 this.downloadErrorPolicies = downloadErrorPolicies;