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
90 * plexus.component role-hint="default"
92 @Service( "repositoryProxyConnectors#default" )
93 public class DefaultRepositoryProxyConnectors
94 implements RepositoryProxyConnectors, RegistryListener
96 private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class );
102 @Named( value = "archivaConfiguration#default" )
103 private ArchivaConfiguration archivaConfiguration;
109 @Named( value = "repositoryContentFactory#default" )
110 private RepositoryContentFactory repositoryFactory;
116 @Named( value = "metadataTools#default" )
117 private MetadataTools metadataTools;
120 //private ApplicationContext applicationContext;
123 * plexus.requirement role="org.apache.maven.archiva.policies.PreDownloadPolicy"
126 private Map<String, PreDownloadPolicy> preDownloadPolicies;
129 * plexus.requirement role="org.apache.maven.archiva.policies.PostDownloadPolicy"
132 private Map<String, PostDownloadPolicy> postDownloadPolicies;
135 * plexus.requirement role="org.apache.maven.archiva.policies.DownloadErrorPolicy"
138 private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
141 * plexus.requirement role-hint="default"
144 private UrlFailureCache urlFailureCache;
146 private Map<String, List<ProxyConnector>> proxyConnectorMap = new HashMap<String, List<ProxyConnector>>();
148 private Map<String, ProxyInfo> networkProxyMap = new HashMap<String, ProxyInfo>();
154 private WagonFactory wagonFactory;
157 * plexus.requirement role="org.apache.archiva.scheduler.ArchivaTaskScheduler" role-hint="repository"
160 @Named( value = "archivaTaskScheduler#repository" )
161 private ArchivaTaskScheduler scheduler;
164 public void initialize()
166 initConnectorsAndNetworkProxies();
167 archivaConfiguration.addChangeListener( this );
168 //this.postDownloadPolicies = applicationContext.getBeansOfType( PostDownloadPolicy.class );
169 //his.preDownloadPolicies = applicationContext.getBeansOfType( PreDownloadPolicy.class );
170 //this.downloadErrorPolicies = applicationContext.getBeansOfType( DownloadErrorPolicy.class );
173 @SuppressWarnings( "unchecked" )
174 private void initConnectorsAndNetworkProxies()
176 synchronized ( this.proxyConnectorMap )
178 ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator();
179 this.proxyConnectorMap.clear();
181 List<ProxyConnectorConfiguration> proxyConfigs =
182 archivaConfiguration.getConfiguration().getProxyConnectors();
183 for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs )
185 String key = proxyConfig.getSourceRepoId();
189 // Create connector object.
190 ProxyConnector connector = new ProxyConnector();
192 connector.setSourceRepository(
193 repositoryFactory.getManagedRepositoryContent( proxyConfig.getSourceRepoId() ) );
194 connector.setTargetRepository(
195 repositoryFactory.getRemoteRepositoryContent( proxyConfig.getTargetRepoId() ) );
197 connector.setProxyId( proxyConfig.getProxyId() );
198 connector.setPolicies( proxyConfig.getPolicies() );
199 connector.setOrder( proxyConfig.getOrder() );
200 connector.setDisabled( proxyConfig.isDisabled() );
202 // Copy any blacklist patterns.
203 List<String> blacklist = new ArrayList<String>();
204 if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) )
206 blacklist.addAll( proxyConfig.getBlackListPatterns() );
208 connector.setBlacklist( blacklist );
210 // Copy any whitelist patterns.
211 List<String> whitelist = new ArrayList<String>();
212 if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) )
214 whitelist.addAll( proxyConfig.getWhiteListPatterns() );
216 connector.setWhitelist( whitelist );
218 // Get other connectors
219 List<ProxyConnector> connectors = this.proxyConnectorMap.get( key );
220 if ( connectors == null )
222 // Create if we are the first.
223 connectors = new ArrayList<ProxyConnector>();
226 // Add the connector.
227 connectors.add( connector );
229 // Ensure the list is sorted.
230 Collections.sort( connectors, proxyOrderSorter );
232 // Set the key to the list of connectors.
233 this.proxyConnectorMap.put( key, connectors );
235 catch ( RepositoryNotFoundException e )
237 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
239 catch ( RepositoryException e )
241 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
247 synchronized ( this.networkProxyMap )
249 this.networkProxyMap.clear();
251 List<NetworkProxyConfiguration> networkProxies =
252 archivaConfiguration.getConfiguration().getNetworkProxies();
253 for ( NetworkProxyConfiguration networkProxyConfig : networkProxies )
255 String key = networkProxyConfig.getId();
257 ProxyInfo proxy = new ProxyInfo();
259 proxy.setType( networkProxyConfig.getProtocol() );
260 proxy.setHost( networkProxyConfig.getHost() );
261 proxy.setPort( networkProxyConfig.getPort() );
262 proxy.setUserName( networkProxyConfig.getUsername() );
263 proxy.setPassword( networkProxyConfig.getPassword() );
265 this.networkProxyMap.put( key, proxy );
270 public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
271 throws ProxyDownloadException
273 File localFile = toLocalFile( repository, artifact );
275 Properties requestProperties = new Properties();
276 requestProperties.setProperty( "filetype", "artifact" );
277 requestProperties.setProperty( "version", artifact.getVersion() );
278 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
280 List<ProxyConnector> connectors = getProxyConnectors( repository );
281 Map<String, Exception> previousExceptions = new LinkedHashMap<String, Exception>();
282 for ( ProxyConnector connector : connectors )
284 if ( connector.isDisabled() )
289 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
290 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
292 String targetPath = targetRepository.toPath( artifact );
296 File downloadedFile =
297 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
300 if ( fileExists( downloadedFile ) )
302 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
303 return downloadedFile;
306 catch ( NotFoundException e )
308 log.debug( "Artifact {} not found on repository \"{}\".", Keys.toKey( artifact ),
309 targetRepository.getRepository().getId() );
311 catch ( NotModifiedException e )
313 log.debug( "Artifact {} not updated on repository \"{}\".", Keys.toKey( artifact ),
314 targetRepository.getRepository().getId() );
316 catch ( ProxyException e )
318 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
319 targetRepository, localFile, e, previousExceptions );
323 if ( !previousExceptions.isEmpty() )
325 throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories",
326 previousExceptions );
329 log.debug( "Exhausted all target repositories, artifact {} not found.", Keys.toKey( artifact ) );
334 public File fetchFromProxies( ManagedRepositoryContent repository, String path )
336 File localFile = new File( repository.getRepoRoot(), path );
338 // no update policies for these paths
339 if ( localFile.exists() )
344 Properties requestProperties = new Properties();
345 requestProperties.setProperty( "filetype", "resource" );
346 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
348 List<ProxyConnector> connectors = getProxyConnectors( repository );
349 for ( ProxyConnector connector : connectors )
351 if ( connector.isDisabled() )
356 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
357 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
359 String targetPath = path;
363 File downloadedFile =
364 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
367 if ( fileExists( downloadedFile ) )
369 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
370 return downloadedFile;
373 catch ( NotFoundException e )
375 log.debug( "Resource {} not found on repository \"{}\".", path,
376 targetRepository.getRepository().getId() );
378 catch ( NotModifiedException e )
380 log.debug( "Resource {} not updated on repository \"{}\".", path,
381 targetRepository.getRepository().getId() );
383 catch ( ProxyException e )
386 "Transfer error from repository \"" + targetRepository.getRepository().getId() + "\" for resource "
387 + path + ", continuing to next repository. Error message: " + e.getMessage() );
388 log.debug( "Full stack trace", e );
392 log.debug( "Exhausted all target repositories, resource {} not found.", path );
397 public File fetchMetatadaFromProxies( ManagedRepositoryContent repository, String logicalPath )
399 File localFile = new File( repository.getRepoRoot(), logicalPath );
401 Properties requestProperties = new Properties();
402 requestProperties.setProperty( "filetype", "metadata" );
403 boolean metadataNeedsUpdating = false;
404 long originalTimestamp = getLastModified( localFile );
406 List<ProxyConnector> connectors = getProxyConnectors( repository );
407 for ( ProxyConnector connector : connectors )
409 if ( connector.isDisabled() )
414 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
416 File localRepoFile = toLocalRepoFile( repository, targetRepository, logicalPath );
417 long originalMetadataTimestamp = getLastModified( localRepoFile );
421 transferFile( connector, targetRepository, logicalPath, repository, localRepoFile, requestProperties,
424 if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
426 metadataNeedsUpdating = true;
429 catch ( NotFoundException e )
431 if ( log.isDebugEnabled() )
433 log.debug( "Metadata {} not found on remote repository \"{}\".",
434 Lists.<Object>newArrayList( logicalPath, targetRepository.getRepository().getId() ), e );
437 catch ( NotModifiedException e )
439 if ( log.isDebugEnabled() )
441 log.debug( "Metadata {} not updated on remote repository \"{}\".",
442 Lists.<Object>newArrayList( logicalPath, targetRepository.getRepository().getId() ), e );
445 catch ( ProxyException e )
447 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId()
448 + "\" for versioned Metadata " + logicalPath
449 + ", continuing to next repository. Error message: " + e.getMessage() );
450 log.debug( "Full stack trace", e );
454 if ( hasBeenUpdated( localFile, originalTimestamp ) )
456 metadataNeedsUpdating = true;
459 if ( metadataNeedsUpdating || !localFile.exists() )
463 metadataTools.updateMetadata( repository, logicalPath );
465 catch ( RepositoryMetadataException e )
467 log.warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
471 if ( fileExists( localFile ) )
479 private long getLastModified( File file )
481 if ( !file.exists() || !file.isFile() )
486 return file.lastModified();
489 private boolean hasBeenUpdated( File file, long originalLastModified )
491 if ( !file.exists() || !file.isFile() )
496 long currentLastModified = getLastModified( file );
497 return ( currentLastModified > originalLastModified );
500 private File toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository,
503 String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath );
504 return new File( repository.getRepoRoot(), repoPath );
508 * Test if the provided ManagedRepositoryContent has any proxies configured for it.
510 public boolean hasProxies( ManagedRepositoryContent repository )
512 synchronized ( this.proxyConnectorMap )
514 return this.proxyConnectorMap.containsKey( repository.getId() );
518 private File toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact )
520 return repository.toFile( artifact );
524 * Simple method to test if the file exists on the local disk.
526 * @param file the file to test. (may be null)
527 * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File.
529 private boolean fileExists( File file )
536 if ( !file.exists() )
541 if ( !file.isFile() )
550 * Perform the transfer of the file.
552 * @param connector the connector configuration to use.
553 * @param remoteRepository the remote repository get the resource from.
554 * @param remotePath the path in the remote repository to the resource to get.
555 * @param repository the managed repository that will hold the file
556 * @param resource the local file to place the downloaded resource into
557 * @param requestProperties the request properties to utilize for policy handling.
558 * @param executeConsumers whether to execute the consumers after proxying
559 * @return the local file that was downloaded, or null if not downloaded.
560 * @throws NotFoundException if the file was not found on the remote repository.
561 * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository, but
562 * the remote resource is not newer than the local File.
563 * @throws ProxyException if transfer was unsuccessful.
565 private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath,
566 ManagedRepositoryContent repository, File resource, Properties requestProperties,
567 boolean executeConsumers )
568 throws ProxyException, NotModifiedException
570 String url = remoteRepository.getURL().getUrl();
571 if ( !url.endsWith( "/" ) )
575 url = url + remotePath;
576 requestProperties.setProperty( "url", url );
578 // Is a whitelist defined?
579 if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
581 // Path must belong to whitelist.
582 if ( !matchesPattern( remotePath, connector.getWhitelist() ) )
584 log.debug( "Path [{}] is not part of defined whitelist (skipping transfer from repository [{}]).",
585 remotePath, remoteRepository.getRepository().getName() );
590 // Is target path part of blacklist?
591 if ( matchesPattern( remotePath, connector.getBlacklist() ) )
593 log.debug( "Path [{}] is part of blacklist (skipping transfer from repository [{}]).", remotePath,
594 remoteRepository.getRepository().getName() );
598 // Handle pre-download policy
601 validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, resource );
603 catch ( PolicyViolationException e )
605 String emsg = "Transfer not attempted on " + url + " : " + e.getMessage();
606 if ( fileExists( resource ) )
608 log.debug( "{} : using already present local file.", emsg );
618 File tmpResource = null;
620 File workingDirectory = createWorkingDirectory( repository );
626 RepositoryURL repoUrl = remoteRepository.getURL();
627 String protocol = repoUrl.getProtocol();
628 wagon = wagonFactory.getWagon( "wagon#" + protocol );
631 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
634 boolean connected = connectToRepository( connector, wagon, remoteRepository );
637 tmpResource = new File( workingDirectory, resource.getName() );
638 transferSimpleFile( wagon, remoteRepository, remotePath, repository, resource, tmpResource );
640 // TODO: these should be used to validate the download based on the policies, not always downloaded
642 // save on connections since md5 is rarely used
644 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
647 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
651 catch ( NotFoundException e )
653 urlFailureCache.cacheFailure( url );
656 catch ( NotModifiedException e )
658 // Do not cache url here.
661 catch ( ProxyException e )
663 urlFailureCache.cacheFailure( url );
666 catch ( WagonFactoryException e )
668 throw new ProxyException( e.getMessage(), e );
678 catch ( ConnectionException e )
680 log.warn( "Unable to disconnect wagon.", e );
685 // Handle post-download policies.
688 validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, tmpResource );
690 catch ( PolicyViolationException e )
692 log.warn( "Transfer invalidated from {} : {}", url, e.getMessage() );
693 executeConsumers = false;
694 if ( !fileExists( tmpResource ) )
700 if ( resource != null )
702 synchronized ( resource.getAbsolutePath().intern() )
704 File directory = resource.getParentFile();
705 moveFileIfExists( tmpMd5, directory );
706 moveFileIfExists( tmpSha1, directory );
707 moveFileIfExists( tmpResource, directory );
713 FileUtils.deleteQuietly( workingDirectory );
716 if ( executeConsumers )
718 // Just-in-time update of the index and database by executing the consumers for this artifact
719 //consumers.executeConsumers( connector.getSourceRepository().getRepository(), resource );
720 queueRepositoryTask( connector.getSourceRepository().getRepository().getId(), resource );
726 private void queueRepositoryTask( String repositoryId, File localFile )
728 RepositoryTask task = new RepositoryTask();
729 task.setRepositoryId( repositoryId );
730 task.setResourceFile( localFile );
731 task.setUpdateRelatedArtifacts( true );
732 task.setScanAll( true );
736 scheduler.queueTask( task );
738 catch ( TaskQueueException e )
740 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
746 * Moves the file into repository location if it exists
748 * @param fileToMove this could be either the main artifact, sha1 or md5 checksum file.
749 * @param directory directory to write files to
751 private void moveFileIfExists( File fileToMove, File directory )
752 throws ProxyException
754 if ( fileToMove != null && fileToMove.exists() )
756 File newLocation = new File( directory, fileToMove.getName() );
757 moveTempToTarget( fileToMove, newLocation );
763 * Quietly transfer the checksum file from the remote repository to the local file.
766 * @param wagon the wagon instance (should already be connected) to use.
767 * @param remoteRepository the remote repository to transfer from.
768 * @param remotePath the remote path to the resource to get.
769 * @param repository the managed repository that will hold the file
770 * @param resource the local file that should contain the downloaded contents
771 * @param tmpDirectory the temporary directory to download to
772 * @param ext the type of checksum to transfer (example: ".md5" or ".sha1")
773 * @throws ProxyException if copying the downloaded file into place did not succeed.
775 private File transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
776 ManagedRepositoryContent repository, File resource, File tmpDirectory, String ext )
777 throws ProxyException
779 String url = remoteRepository.getURL().getUrl() + remotePath + ext;
781 // Transfer checksum does not use the policy.
782 if ( urlFailureCache.hasFailedBefore( url ) )
787 File destFile = new File( tmpDirectory, resource.getName() + ext );
791 transferSimpleFile( wagon, remoteRepository, remotePath + ext, repository, resource, destFile );
792 log.debug( "Checksum {} Downloaded: {} to move to {}", Arrays.asList( url, destFile, resource ).toArray() );
794 catch ( NotFoundException e )
796 urlFailureCache.cacheFailure( url );
797 log.debug( "Transfer failed, checksum not found: {}", url );
798 // Consume it, do not pass this on.
800 catch ( NotModifiedException e )
802 log.debug( "Transfer skipped, checksum not modified: {}", url );
803 // Consume it, do not pass this on.
805 catch ( ProxyException e )
807 urlFailureCache.cacheFailure( url );
808 log.warn( "Transfer failed on checksum: " + url + " : " + e.getMessage(), e );
809 // Critical issue, pass it on.
816 * Perform the transfer of the remote file to the local file specified.
818 * @param wagon the wagon instance to use.
819 * @param remoteRepository the remote repository to use
820 * @param remotePath the remote path to attempt to get
821 * @param repository the managed repository that will hold the file
822 * @param origFile the local file to save to
823 * @return The local file that was transfered.
824 * @throws ProxyException if there was a problem moving the downloaded file into place.
825 * @throws WagonException if there was a problem tranfering the file.
827 private void transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
828 ManagedRepositoryContent repository, File origFile, File destFile )
829 throws ProxyException
831 assert ( remotePath != null );
833 // Transfer the file.
836 boolean success = false;
838 if ( !origFile.exists() )
840 log.debug( "Retrieving {} from {}", remotePath, remoteRepository.getRepository().getName() );
841 wagon.get( remotePath, destFile );
844 // You wouldn't get here on failure, a WagonException would have been thrown.
845 log.debug( "Downloaded successfully." );
849 log.debug( "Retrieving {} from {} if updated", remotePath, remoteRepository.getRepository().getName() );
850 success = wagon.getIfNewer( remotePath, destFile, origFile.lastModified() );
853 throw new NotModifiedException(
854 "Not downloaded, as local file is newer than remote side: " + origFile.getAbsolutePath() );
857 if ( destFile.exists() )
859 log.debug( "Downloaded successfully." );
863 catch ( ResourceDoesNotExistException e )
865 throw new NotFoundException(
866 "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(),
869 catch ( WagonException e )
871 // TODO: shouldn't have to drill into the cause, but TransferFailedException is often not descriptive enough
874 "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage();
875 if ( e.getCause() != null )
877 msg += " (cause: " + e.getCause() + ")";
879 throw new ProxyException( msg, e );
884 * Apply the policies.
886 * @param policies the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
887 * @param settings the map of settings for the policies to execute. (Map of String policy keys, to String policy
889 * @param request the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, File)}
891 * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String, Properties, File)})
893 private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings,
894 Properties request, File localFile )
895 throws PolicyViolationException
897 for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
899 // olamy with spring rolehint is now downloadPolicy#hint
900 // so substring after last # to get the hint as with plexus
901 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
902 DownloadPolicy policy = entry.getValue();
903 String defaultSetting = policy.getDefaultOption();
905 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
907 log.debug( "Applying [{}] policy with [{}]", key, setting );
910 policy.applyPolicy( setting, request, localFile );
912 catch ( PolicyConfigurationException e )
914 log.error( e.getMessage(), e );
919 private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings,
920 Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
921 File localFile, ProxyException exception, Map<String, Exception> previousExceptions )
922 throws ProxyDownloadException
924 boolean process = true;
925 for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
928 // olamy with spring rolehint is now downloadPolicy#hint
929 // so substring after last # to get the hint as with plexus
930 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
931 DownloadErrorPolicy policy = entry.getValue();
932 String defaultSetting = policy.getDefaultOption();
933 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
935 log.debug( "Applying [{}] policy with [{}]", key, setting );
938 // all policies must approve the exception, any can cancel
939 process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions );
945 catch ( PolicyConfigurationException e )
947 log.error( e.getMessage(), e );
953 // if the exception was queued, don't throw it
954 if ( !previousExceptions.containsKey( content.getId() ) )
956 throw new ProxyDownloadException(
957 "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
958 content.getId(), exception );
963 // if the exception was queued, but cancelled, remove it
964 previousExceptions.remove( content.getId() );
967 log.warn( "Transfer error from repository \"" + content.getRepository().getId() + "\" for artifact "
968 + Keys.toKey( artifact ) + ", continuing to next repository. Error message: "
969 + exception.getMessage() );
970 log.debug( "Full stack trace", exception );
974 * Creates a working directory in the repository root for this request
977 * @return file location of working directory
978 * @throws IOException
980 private File createWorkingDirectory( ManagedRepositoryContent repository )
982 // TODO: This is ugly - lets actually clean this up when we get the new repository api
985 File tmpDir = File.createTempFile( ".workingdirectory", null, new File( repository.getRepoRoot() ) );
990 catch ( IOException e )
992 throw new RuntimeException( "Could not create working directory for this request", e );
997 * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles its
1000 * @param temp The completed download file
1001 * @param target The final location of the downloaded file
1002 * @throws ProxyException when the temp file cannot replace the target file
1004 private void moveTempToTarget( File temp, File target )
1005 throws ProxyException
1007 if ( target.exists() && !target.delete() )
1009 throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() );
1012 target.getParentFile().mkdirs();
1013 if ( !temp.renameTo( target ) )
1015 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
1019 FileUtils.copyFile( temp, target );
1021 catch ( IOException e )
1023 if ( target.exists() )
1025 log.debug( "Tried to copy file {} to {} but file with this name already exists.", temp.getName(),
1026 target.getAbsolutePath() );
1030 throw new ProxyException(
1031 "Cannot copy tmp file " + temp.getAbsolutePath() + " to its final location", e );
1036 FileUtils.deleteQuietly( temp );
1042 * Using wagon, connect to the remote repository.
1044 * @param connector the connector configuration to utilize (for obtaining network proxy configuration from)
1045 * @param wagon the wagon instance to establish the connection on.
1046 * @param remoteRepository the remote repository to connect to.
1047 * @return true if the connection was successful. false if not connected.
1049 private boolean connectToRepository( ProxyConnector connector, Wagon wagon,
1050 RemoteRepositoryContent remoteRepository )
1052 boolean connected = false;
1054 final ProxyInfo networkProxy;
1055 synchronized ( this.networkProxyMap )
1057 networkProxy = (ProxyInfo) this.networkProxyMap.get( connector.getProxyId() );
1060 if ( log.isDebugEnabled() )
1062 if ( networkProxy != null )
1064 // TODO: move to proxyInfo.toString()
1065 String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
1066 + " to connect to remote repository " + remoteRepository.getURL();
1067 if ( networkProxy.getNonProxyHosts() != null )
1069 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
1071 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
1073 msg += "; as user: " + networkProxy.getUserName();
1079 AuthenticationInfo authInfo = null;
1080 String username = remoteRepository.getRepository().getUsername();
1081 String password = remoteRepository.getRepository().getPassword();
1083 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
1085 log.debug( "Using username {} to connect to remote repository {}", username, remoteRepository.getURL() );
1086 authInfo = new AuthenticationInfo();
1087 authInfo.setUserName( username );
1088 authInfo.setPassword( password );
1091 // Convert seconds to milliseconds
1092 int timeoutInMilliseconds = remoteRepository.getRepository().getTimeout() * 1000;
1095 wagon.setTimeout( timeoutInMilliseconds );
1099 Repository wagonRepository =
1100 new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
1101 wagon.connect( wagonRepository, authInfo, networkProxy );
1104 catch ( ConnectionException e )
1106 log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1109 catch ( AuthenticationException e )
1111 log.warn( "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1119 * Tests whitelist and blacklist patterns against path.
1121 * @param path the path to test.
1122 * @param patterns the list of patterns to check.
1123 * @return true if the path matches at least 1 pattern in the provided patterns list.
1125 private boolean matchesPattern( String path, List<String> patterns )
1127 if ( CollectionUtils.isEmpty( patterns ) )
1132 if ( !path.startsWith( "/" ) )
1137 for ( String pattern : patterns )
1139 if ( !pattern.startsWith( "/" ) )
1141 pattern = "/" + pattern;
1144 if ( SelectorUtils.matchPath( pattern, path, false ) )
1154 * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
1156 public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository )
1158 synchronized ( this.proxyConnectorMap )
1160 List<ProxyConnector> ret = (List<ProxyConnector>) this.proxyConnectorMap.get( repository.getId() );
1163 return Collections.emptyList();
1166 Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
1171 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1173 if ( ConfigurationNames.isNetworkProxy( propertyName )
1174 || ConfigurationNames.isManagedRepositories( propertyName )
1175 || ConfigurationNames.isRemoteRepositories( propertyName ) || ConfigurationNames.isProxyConnector(
1178 initConnectorsAndNetworkProxies();
1182 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1187 public ArchivaConfiguration getArchivaConfiguration()
1189 return archivaConfiguration;
1192 public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
1194 this.archivaConfiguration = archivaConfiguration;
1197 public RepositoryContentFactory getRepositoryFactory()
1199 return repositoryFactory;
1202 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
1204 this.repositoryFactory = repositoryFactory;
1207 public MetadataTools getMetadataTools()
1209 return metadataTools;
1212 public void setMetadataTools( MetadataTools metadataTools )
1214 this.metadataTools = metadataTools;
1217 public UrlFailureCache getUrlFailureCache()
1219 return urlFailureCache;
1222 public void setUrlFailureCache( UrlFailureCache urlFailureCache )
1224 this.urlFailureCache = urlFailureCache;
1227 public WagonFactory getWagonFactory()
1229 return wagonFactory;
1232 public void setWagonFactory( WagonFactory wagonFactory )
1234 this.wagonFactory = wagonFactory;
1237 public Map<String, PreDownloadPolicy> getPreDownloadPolicies()
1239 return preDownloadPolicies;
1242 public void setPreDownloadPolicies( Map<String, PreDownloadPolicy> preDownloadPolicies )
1244 this.preDownloadPolicies = preDownloadPolicies;
1247 public Map<String, PostDownloadPolicy> getPostDownloadPolicies()
1249 return postDownloadPolicies;
1252 public void setPostDownloadPolicies( Map<String, PostDownloadPolicy> postDownloadPolicies )
1254 this.postDownloadPolicies = postDownloadPolicies;
1257 public Map<String, DownloadErrorPolicy> getDownloadErrorPolicies()
1259 return downloadErrorPolicies;
1262 public void setDownloadErrorPolicies( Map<String, DownloadErrorPolicy> downloadErrorPolicies )
1264 this.downloadErrorPolicies = downloadErrorPolicies;