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;
122 private Map<String, PreDownloadPolicy> preDownloadPolicies;
128 private Map<String, PostDownloadPolicy> postDownloadPolicies;
134 private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
140 private UrlFailureCache urlFailureCache;
142 private Map<String, List<ProxyConnector>> proxyConnectorMap = new HashMap<String, List<ProxyConnector>>();
144 private Map<String, ProxyInfo> networkProxyMap = new HashMap<String, ProxyInfo>();
150 private WagonFactory wagonFactory;
156 @Named( value = "archivaTaskScheduler#repository" )
157 private ArchivaTaskScheduler scheduler;
160 public void initialize()
162 initConnectorsAndNetworkProxies();
163 archivaConfiguration.addChangeListener( this );
164 //this.postDownloadPolicies = applicationContext.getBeansOfType( PostDownloadPolicy.class );
165 //his.preDownloadPolicies = applicationContext.getBeansOfType( PreDownloadPolicy.class );
166 //this.downloadErrorPolicies = applicationContext.getBeansOfType( DownloadErrorPolicy.class );
169 @SuppressWarnings( "unchecked" )
170 private void initConnectorsAndNetworkProxies()
172 synchronized ( this.proxyConnectorMap )
174 ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator();
175 this.proxyConnectorMap.clear();
177 List<ProxyConnectorConfiguration> proxyConfigs =
178 archivaConfiguration.getConfiguration().getProxyConnectors();
179 for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs )
181 String key = proxyConfig.getSourceRepoId();
185 // Create connector object.
186 ProxyConnector connector = new ProxyConnector();
188 connector.setSourceRepository(
189 repositoryFactory.getManagedRepositoryContent( proxyConfig.getSourceRepoId() ) );
190 connector.setTargetRepository(
191 repositoryFactory.getRemoteRepositoryContent( proxyConfig.getTargetRepoId() ) );
193 connector.setProxyId( proxyConfig.getProxyId() );
194 connector.setPolicies( proxyConfig.getPolicies() );
195 connector.setOrder( proxyConfig.getOrder() );
196 connector.setDisabled( proxyConfig.isDisabled() );
198 // Copy any blacklist patterns.
199 List<String> blacklist = new ArrayList<String>();
200 if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) )
202 blacklist.addAll( proxyConfig.getBlackListPatterns() );
204 connector.setBlacklist( blacklist );
206 // Copy any whitelist patterns.
207 List<String> whitelist = new ArrayList<String>();
208 if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) )
210 whitelist.addAll( proxyConfig.getWhiteListPatterns() );
212 connector.setWhitelist( whitelist );
214 // Get other connectors
215 List<ProxyConnector> connectors = this.proxyConnectorMap.get( key );
216 if ( connectors == null )
218 // Create if we are the first.
219 connectors = new ArrayList<ProxyConnector>();
222 // Add the connector.
223 connectors.add( connector );
225 // Ensure the list is sorted.
226 Collections.sort( connectors, proxyOrderSorter );
228 // Set the key to the list of connectors.
229 this.proxyConnectorMap.put( key, connectors );
231 catch ( RepositoryNotFoundException e )
233 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
235 catch ( RepositoryException e )
237 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
243 synchronized ( this.networkProxyMap )
245 this.networkProxyMap.clear();
247 List<NetworkProxyConfiguration> networkProxies =
248 archivaConfiguration.getConfiguration().getNetworkProxies();
249 for ( NetworkProxyConfiguration networkProxyConfig : networkProxies )
251 String key = networkProxyConfig.getId();
253 ProxyInfo proxy = new ProxyInfo();
255 proxy.setType( networkProxyConfig.getProtocol() );
256 proxy.setHost( networkProxyConfig.getHost() );
257 proxy.setPort( networkProxyConfig.getPort() );
258 proxy.setUserName( networkProxyConfig.getUsername() );
259 proxy.setPassword( networkProxyConfig.getPassword() );
261 this.networkProxyMap.put( key, proxy );
266 public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
267 throws ProxyDownloadException
269 File localFile = toLocalFile( repository, artifact );
271 Properties requestProperties = new Properties();
272 requestProperties.setProperty( "filetype", "artifact" );
273 requestProperties.setProperty( "version", artifact.getVersion() );
274 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
276 List<ProxyConnector> connectors = getProxyConnectors( repository );
277 Map<String, Exception> previousExceptions = new LinkedHashMap<String, Exception>();
278 for ( ProxyConnector connector : connectors )
280 if ( connector.isDisabled() )
285 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
286 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
288 String targetPath = targetRepository.toPath( artifact );
292 File downloadedFile =
293 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
296 if ( fileExists( downloadedFile ) )
298 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
299 return downloadedFile;
302 catch ( NotFoundException e )
304 log.debug( "Artifact {} not found on repository \"{}\".", Keys.toKey( artifact ),
305 targetRepository.getRepository().getId() );
307 catch ( NotModifiedException e )
309 log.debug( "Artifact {} not updated on repository \"{}\".", Keys.toKey( artifact ),
310 targetRepository.getRepository().getId() );
312 catch ( ProxyException e )
314 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
315 targetRepository, localFile, e, previousExceptions );
319 if ( !previousExceptions.isEmpty() )
321 throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories",
322 previousExceptions );
325 log.debug( "Exhausted all target repositories, artifact {} not found.", Keys.toKey( artifact ) );
330 public File fetchFromProxies( ManagedRepositoryContent repository, String path )
332 File localFile = new File( repository.getRepoRoot(), path );
334 // no update policies for these paths
335 if ( localFile.exists() )
340 Properties requestProperties = new Properties();
341 requestProperties.setProperty( "filetype", "resource" );
342 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
344 List<ProxyConnector> connectors = getProxyConnectors( repository );
345 for ( ProxyConnector connector : connectors )
347 if ( connector.isDisabled() )
352 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
353 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
355 String targetPath = path;
359 File downloadedFile =
360 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
363 if ( fileExists( downloadedFile ) )
365 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
366 return downloadedFile;
369 catch ( NotFoundException e )
371 log.debug( "Resource {} not found on repository \"{}\".", path,
372 targetRepository.getRepository().getId() );
374 catch ( NotModifiedException e )
376 log.debug( "Resource {} not updated on repository \"{}\".", path,
377 targetRepository.getRepository().getId() );
379 catch ( ProxyException e )
382 "Transfer error from repository \"" + targetRepository.getRepository().getId() + "\" for resource "
383 + path + ", continuing to next repository. Error message: " + e.getMessage() );
384 log.debug( "Full stack trace", e );
388 log.debug( "Exhausted all target repositories, resource {} not found.", path );
393 public File fetchMetatadaFromProxies( ManagedRepositoryContent repository, String logicalPath )
395 File localFile = new File( repository.getRepoRoot(), logicalPath );
397 Properties requestProperties = new Properties();
398 requestProperties.setProperty( "filetype", "metadata" );
399 boolean metadataNeedsUpdating = false;
400 long originalTimestamp = getLastModified( localFile );
402 List<ProxyConnector> connectors = getProxyConnectors( repository );
403 for ( ProxyConnector connector : connectors )
405 if ( connector.isDisabled() )
410 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
412 File localRepoFile = toLocalRepoFile( repository, targetRepository, logicalPath );
413 long originalMetadataTimestamp = getLastModified( localRepoFile );
417 transferFile( connector, targetRepository, logicalPath, repository, localRepoFile, requestProperties,
420 if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
422 metadataNeedsUpdating = true;
425 catch ( NotFoundException e )
427 if ( log.isDebugEnabled() )
429 log.debug( "Metadata {} not found on remote repository \"{}\".",
430 Lists.<Object>newArrayList( logicalPath, targetRepository.getRepository().getId() ), e );
433 catch ( NotModifiedException e )
435 if ( log.isDebugEnabled() )
437 log.debug( "Metadata {} not updated on remote repository \"{}\".",
438 Lists.<Object>newArrayList( logicalPath, targetRepository.getRepository().getId() ), e );
441 catch ( ProxyException e )
443 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId()
444 + "\" for versioned Metadata " + logicalPath
445 + ", continuing to next repository. Error message: " + e.getMessage() );
446 log.debug( "Full stack trace", e );
450 if ( hasBeenUpdated( localFile, originalTimestamp ) )
452 metadataNeedsUpdating = true;
455 if ( metadataNeedsUpdating || !localFile.exists() )
459 metadataTools.updateMetadata( repository, logicalPath );
461 catch ( RepositoryMetadataException e )
463 log.warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
467 if ( fileExists( localFile ) )
475 private long getLastModified( File file )
477 if ( !file.exists() || !file.isFile() )
482 return file.lastModified();
485 private boolean hasBeenUpdated( File file, long originalLastModified )
487 if ( !file.exists() || !file.isFile() )
492 long currentLastModified = getLastModified( file );
493 return ( currentLastModified > originalLastModified );
496 private File toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository,
499 String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath );
500 return new File( repository.getRepoRoot(), repoPath );
504 * Test if the provided ManagedRepositoryContent has any proxies configured for it.
506 public boolean hasProxies( ManagedRepositoryContent repository )
508 synchronized ( this.proxyConnectorMap )
510 return this.proxyConnectorMap.containsKey( repository.getId() );
514 private File toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact )
516 return repository.toFile( artifact );
520 * Simple method to test if the file exists on the local disk.
522 * @param file the file to test. (may be null)
523 * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File.
525 private boolean fileExists( File file )
532 if ( !file.exists() )
537 if ( !file.isFile() )
546 * Perform the transfer of the file.
548 * @param connector the connector configuration to use.
549 * @param remoteRepository the remote repository get the resource from.
550 * @param remotePath the path in the remote repository to the resource to get.
551 * @param repository the managed repository that will hold the file
552 * @param resource the local file to place the downloaded resource into
553 * @param requestProperties the request properties to utilize for policy handling.
554 * @param executeConsumers whether to execute the consumers after proxying
555 * @return the local file that was downloaded, or null if not downloaded.
556 * @throws NotFoundException if the file was not found on the remote repository.
557 * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository, but
558 * the remote resource is not newer than the local File.
559 * @throws ProxyException if transfer was unsuccessful.
561 private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath,
562 ManagedRepositoryContent repository, File resource, Properties requestProperties,
563 boolean executeConsumers )
564 throws ProxyException, NotModifiedException
566 String url = remoteRepository.getURL().getUrl();
567 if ( !url.endsWith( "/" ) )
571 url = url + remotePath;
572 requestProperties.setProperty( "url", url );
574 // Is a whitelist defined?
575 if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
577 // Path must belong to whitelist.
578 if ( !matchesPattern( remotePath, connector.getWhitelist() ) )
580 log.debug( "Path [{}] is not part of defined whitelist (skipping transfer from repository [{}]).",
581 remotePath, remoteRepository.getRepository().getName() );
586 // Is target path part of blacklist?
587 if ( matchesPattern( remotePath, connector.getBlacklist() ) )
589 log.debug( "Path [{}] is part of blacklist (skipping transfer from repository [{}]).", remotePath,
590 remoteRepository.getRepository().getName() );
594 // Handle pre-download policy
597 validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, resource );
599 catch ( PolicyViolationException e )
601 String emsg = "Transfer not attempted on " + url + " : " + e.getMessage();
602 if ( fileExists( resource ) )
604 log.debug( "{} : using already present local file.", emsg );
614 File tmpResource = null;
616 File workingDirectory = createWorkingDirectory( repository );
622 RepositoryURL repoUrl = remoteRepository.getURL();
623 String protocol = repoUrl.getProtocol();
624 wagon = wagonFactory.getWagon( "wagon#" + protocol );
627 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
630 boolean connected = connectToRepository( connector, wagon, remoteRepository );
633 tmpResource = new File( workingDirectory, resource.getName() );
634 transferSimpleFile( wagon, remoteRepository, remotePath, repository, resource, tmpResource );
636 // TODO: these should be used to validate the download based on the policies, not always downloaded
638 // save on connections since md5 is rarely used
640 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
643 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
647 catch ( NotFoundException e )
649 urlFailureCache.cacheFailure( url );
652 catch ( NotModifiedException e )
654 // Do not cache url here.
657 catch ( ProxyException e )
659 urlFailureCache.cacheFailure( url );
662 catch ( WagonFactoryException e )
664 throw new ProxyException( e.getMessage(), e );
674 catch ( ConnectionException e )
676 log.warn( "Unable to disconnect wagon.", e );
681 // Handle post-download policies.
684 validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, tmpResource );
686 catch ( PolicyViolationException e )
688 log.warn( "Transfer invalidated from {} : {}", url, e.getMessage() );
689 executeConsumers = false;
690 if ( !fileExists( tmpResource ) )
696 if ( resource != null )
698 synchronized ( resource.getAbsolutePath().intern() )
700 File directory = resource.getParentFile();
701 moveFileIfExists( tmpMd5, directory );
702 moveFileIfExists( tmpSha1, directory );
703 moveFileIfExists( tmpResource, directory );
709 FileUtils.deleteQuietly( workingDirectory );
712 if ( executeConsumers )
714 // Just-in-time update of the index and database by executing the consumers for this artifact
715 //consumers.executeConsumers( connector.getSourceRepository().getRepository(), resource );
716 queueRepositoryTask( connector.getSourceRepository().getRepository().getId(), resource );
722 private void queueRepositoryTask( String repositoryId, File localFile )
724 RepositoryTask task = new RepositoryTask();
725 task.setRepositoryId( repositoryId );
726 task.setResourceFile( localFile );
727 task.setUpdateRelatedArtifacts( true );
728 task.setScanAll( true );
732 scheduler.queueTask( task );
734 catch ( TaskQueueException e )
736 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
742 * Moves the file into repository location if it exists
744 * @param fileToMove this could be either the main artifact, sha1 or md5 checksum file.
745 * @param directory directory to write files to
747 private void moveFileIfExists( File fileToMove, File directory )
748 throws ProxyException
750 if ( fileToMove != null && fileToMove.exists() )
752 File newLocation = new File( directory, fileToMove.getName() );
753 moveTempToTarget( fileToMove, newLocation );
759 * Quietly transfer the checksum file from the remote repository to the local file.
762 * @param wagon the wagon instance (should already be connected) to use.
763 * @param remoteRepository the remote repository to transfer from.
764 * @param remotePath the remote path to the resource to get.
765 * @param repository the managed repository that will hold the file
766 * @param resource the local file that should contain the downloaded contents
767 * @param tmpDirectory the temporary directory to download to
768 * @param ext the type of checksum to transfer (example: ".md5" or ".sha1")
769 * @throws ProxyException if copying the downloaded file into place did not succeed.
771 private File transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
772 ManagedRepositoryContent repository, File resource, File tmpDirectory, String ext )
773 throws ProxyException
775 String url = remoteRepository.getURL().getUrl() + remotePath + ext;
777 // Transfer checksum does not use the policy.
778 if ( urlFailureCache.hasFailedBefore( url ) )
783 File destFile = new File( tmpDirectory, resource.getName() + ext );
787 transferSimpleFile( wagon, remoteRepository, remotePath + ext, repository, resource, destFile );
788 log.debug( "Checksum {} Downloaded: {} to move to {}", Arrays.asList( url, destFile, resource ).toArray() );
790 catch ( NotFoundException e )
792 urlFailureCache.cacheFailure( url );
793 log.debug( "Transfer failed, checksum not found: {}", url );
794 // Consume it, do not pass this on.
796 catch ( NotModifiedException e )
798 log.debug( "Transfer skipped, checksum not modified: {}", url );
799 // Consume it, do not pass this on.
801 catch ( ProxyException e )
803 urlFailureCache.cacheFailure( url );
804 log.warn( "Transfer failed on checksum: " + url + " : " + e.getMessage(), e );
805 // Critical issue, pass it on.
812 * Perform the transfer of the remote file to the local file specified.
814 * @param wagon the wagon instance to use.
815 * @param remoteRepository the remote repository to use
816 * @param remotePath the remote path to attempt to get
817 * @param repository the managed repository that will hold the file
818 * @param origFile the local file to save to
819 * @return The local file that was transfered.
820 * @throws ProxyException if there was a problem moving the downloaded file into place.
821 * @throws WagonException if there was a problem tranfering the file.
823 private void transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
824 ManagedRepositoryContent repository, File origFile, File destFile )
825 throws ProxyException
827 assert ( remotePath != null );
829 // Transfer the file.
832 boolean success = false;
834 if ( !origFile.exists() )
836 log.debug( "Retrieving {} from {}", remotePath, remoteRepository.getRepository().getName() );
837 wagon.get( remotePath, destFile );
840 // You wouldn't get here on failure, a WagonException would have been thrown.
841 log.debug( "Downloaded successfully." );
845 log.debug( "Retrieving {} from {} if updated", remotePath, remoteRepository.getRepository().getName() );
846 success = wagon.getIfNewer( remotePath, destFile, origFile.lastModified() );
849 throw new NotModifiedException(
850 "Not downloaded, as local file is newer than remote side: " + origFile.getAbsolutePath() );
853 if ( destFile.exists() )
855 log.debug( "Downloaded successfully." );
859 catch ( ResourceDoesNotExistException e )
861 throw new NotFoundException(
862 "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(),
865 catch ( WagonException e )
867 // TODO: shouldn't have to drill into the cause, but TransferFailedException is often not descriptive enough
870 "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage();
871 if ( e.getCause() != null )
873 msg += " (cause: " + e.getCause() + ")";
875 throw new ProxyException( msg, e );
880 * Apply the policies.
882 * @param policies the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
883 * @param settings the map of settings for the policies to execute. (Map of String policy keys, to String policy
885 * @param request the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, File)}
887 * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, File)})
889 private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings,
890 Properties request, File localFile )
891 throws PolicyViolationException
893 for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
895 // olamy with spring rolehint is now downloadPolicy#hint
896 // so substring after last # to get the hint as with plexus
897 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
898 DownloadPolicy policy = entry.getValue();
899 String defaultSetting = policy.getDefaultOption();
901 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
903 log.debug( "Applying [{}] policy with [{}]", key, setting );
906 policy.applyPolicy( setting, request, localFile );
908 catch ( PolicyConfigurationException e )
910 log.error( e.getMessage(), e );
915 private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings,
916 Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
917 File localFile, ProxyException exception, Map<String, Exception> previousExceptions )
918 throws ProxyDownloadException
920 boolean process = true;
921 for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
924 // olamy with spring rolehint is now downloadPolicy#hint
925 // so substring after last # to get the hint as with plexus
926 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
927 DownloadErrorPolicy policy = entry.getValue();
928 String defaultSetting = policy.getDefaultOption();
929 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
931 log.debug( "Applying [{}] policy with [{}]", key, setting );
934 // all policies must approve the exception, any can cancel
935 process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions );
941 catch ( PolicyConfigurationException e )
943 log.error( e.getMessage(), e );
949 // if the exception was queued, don't throw it
950 if ( !previousExceptions.containsKey( content.getId() ) )
952 throw new ProxyDownloadException(
953 "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
954 content.getId(), exception );
959 // if the exception was queued, but cancelled, remove it
960 previousExceptions.remove( content.getId() );
963 log.warn( "Transfer error from repository \"" + content.getRepository().getId() + "\" for artifact "
964 + Keys.toKey( artifact ) + ", continuing to next repository. Error message: "
965 + exception.getMessage() );
966 log.debug( "Full stack trace", exception );
970 * Creates a working directory in the repository root for this request
973 * @return file location of working directory
974 * @throws IOException
976 private File createWorkingDirectory( ManagedRepositoryContent repository )
978 // TODO: This is ugly - lets actually clean this up when we get the new repository api
981 File tmpDir = File.createTempFile( ".workingdirectory", null, new File( repository.getRepoRoot() ) );
986 catch ( IOException e )
988 throw new RuntimeException( "Could not create working directory for this request", e );
993 * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles its
996 * @param temp The completed download file
997 * @param target The final location of the downloaded file
998 * @throws ProxyException when the temp file cannot replace the target file
1000 private void moveTempToTarget( File temp, File target )
1001 throws ProxyException
1003 if ( target.exists() && !target.delete() )
1005 throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() );
1008 target.getParentFile().mkdirs();
1009 if ( !temp.renameTo( target ) )
1011 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
1015 FileUtils.copyFile( temp, target );
1017 catch ( IOException e )
1019 if ( target.exists() )
1021 log.debug( "Tried to copy file {} to {} but file with this name already exists.", temp.getName(),
1022 target.getAbsolutePath() );
1026 throw new ProxyException(
1027 "Cannot copy tmp file " + temp.getAbsolutePath() + " to its final location", e );
1032 FileUtils.deleteQuietly( temp );
1038 * Using wagon, connect to the remote repository.
1040 * @param connector the connector configuration to utilize (for obtaining network proxy configuration from)
1041 * @param wagon the wagon instance to establish the connection on.
1042 * @param remoteRepository the remote repository to connect to.
1043 * @return true if the connection was successful. false if not connected.
1045 private boolean connectToRepository( ProxyConnector connector, Wagon wagon,
1046 RemoteRepositoryContent remoteRepository )
1048 boolean connected = false;
1050 final ProxyInfo networkProxy;
1051 synchronized ( this.networkProxyMap )
1053 networkProxy = (ProxyInfo) this.networkProxyMap.get( connector.getProxyId() );
1056 if ( log.isDebugEnabled() )
1058 if ( networkProxy != null )
1060 // TODO: move to proxyInfo.toString()
1061 String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
1062 + " to connect to remote repository " + remoteRepository.getURL();
1063 if ( networkProxy.getNonProxyHosts() != null )
1065 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
1067 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
1069 msg += "; as user: " + networkProxy.getUserName();
1075 AuthenticationInfo authInfo = null;
1076 String username = remoteRepository.getRepository().getUsername();
1077 String password = remoteRepository.getRepository().getPassword();
1079 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
1081 log.debug( "Using username {} to connect to remote repository {}", username, remoteRepository.getURL() );
1082 authInfo = new AuthenticationInfo();
1083 authInfo.setUserName( username );
1084 authInfo.setPassword( password );
1087 // Convert seconds to milliseconds
1088 int timeoutInMilliseconds = remoteRepository.getRepository().getTimeout() * 1000;
1091 wagon.setTimeout( timeoutInMilliseconds );
1095 Repository wagonRepository =
1096 new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
1097 wagon.connect( wagonRepository, authInfo, networkProxy );
1100 catch ( ConnectionException e )
1102 log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1105 catch ( AuthenticationException e )
1107 log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1115 * Tests whitelist and blacklist patterns against path.
1117 * @param path the path to test.
1118 * @param patterns the list of patterns to check.
1119 * @return true if the path matches at least 1 pattern in the provided patterns list.
1121 private boolean matchesPattern( String path, List<String> patterns )
1123 if ( CollectionUtils.isEmpty( patterns ) )
1128 if ( !path.startsWith( "/" ) )
1133 for ( String pattern : patterns )
1135 if ( !pattern.startsWith( "/" ) )
1137 pattern = "/" + pattern;
1140 if ( SelectorUtils.matchPath( pattern, path, false ) )
1150 * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
1152 public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository )
1154 synchronized ( this.proxyConnectorMap )
1156 List<ProxyConnector> ret = (List<ProxyConnector>) this.proxyConnectorMap.get( repository.getId() );
1159 return Collections.emptyList();
1162 Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
1167 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1169 if ( ConfigurationNames.isNetworkProxy( propertyName )
1170 || ConfigurationNames.isManagedRepositories( propertyName )
1171 || ConfigurationNames.isRemoteRepositories( propertyName ) || ConfigurationNames.isProxyConnector(
1174 initConnectorsAndNetworkProxies();
1178 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1183 public ArchivaConfiguration getArchivaConfiguration()
1185 return archivaConfiguration;
1188 public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
1190 this.archivaConfiguration = archivaConfiguration;
1193 public RepositoryContentFactory getRepositoryFactory()
1195 return repositoryFactory;
1198 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
1200 this.repositoryFactory = repositoryFactory;
1203 public MetadataTools getMetadataTools()
1205 return metadataTools;
1208 public void setMetadataTools( MetadataTools metadataTools )
1210 this.metadataTools = metadataTools;
1213 public UrlFailureCache getUrlFailureCache()
1215 return urlFailureCache;
1218 public void setUrlFailureCache( UrlFailureCache urlFailureCache )
1220 this.urlFailureCache = urlFailureCache;
1223 public WagonFactory getWagonFactory()
1225 return wagonFactory;
1228 public void setWagonFactory( WagonFactory wagonFactory )
1230 this.wagonFactory = wagonFactory;
1233 public Map<String, PreDownloadPolicy> getPreDownloadPolicies()
1235 return preDownloadPolicies;
1238 public void setPreDownloadPolicies( Map<String, PreDownloadPolicy> preDownloadPolicies )
1240 this.preDownloadPolicies = preDownloadPolicies;
1243 public Map<String, PostDownloadPolicy> getPostDownloadPolicies()
1245 return postDownloadPolicies;
1248 public void setPostDownloadPolicies( Map<String, PostDownloadPolicy> postDownloadPolicies )
1250 this.postDownloadPolicies = postDownloadPolicies;
1253 public Map<String, DownloadErrorPolicy> getDownloadErrorPolicies()
1255 return downloadErrorPolicies;
1258 public void setDownloadErrorPolicies( Map<String, DownloadErrorPolicy> downloadErrorPolicies )
1260 this.downloadErrorPolicies = downloadErrorPolicies;