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
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.LinkedHashMap;
28 import java.util.List;
30 import java.util.Properties;
31 import java.util.Map.Entry;
33 import org.apache.commons.collections.CollectionUtils;
34 import org.apache.commons.io.FileUtils;
35 import org.apache.commons.lang.StringUtils;
36 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
37 import org.apache.maven.archiva.configuration.ConfigurationNames;
38 import org.apache.maven.archiva.configuration.NetworkProxyConfiguration;
39 import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration;
40 import org.apache.maven.archiva.model.ArtifactReference;
41 import org.apache.maven.archiva.model.Keys;
42 import org.apache.maven.archiva.model.ProjectReference;
43 import org.apache.maven.archiva.model.RepositoryURL;
44 import org.apache.maven.archiva.model.VersionedReference;
45 import org.apache.maven.archiva.policies.DownloadErrorPolicy;
46 import org.apache.maven.archiva.policies.DownloadPolicy;
47 import org.apache.maven.archiva.policies.PolicyConfigurationException;
48 import org.apache.maven.archiva.policies.PolicyViolationException;
49 import org.apache.maven.archiva.policies.PostDownloadPolicy;
50 import org.apache.maven.archiva.policies.PreDownloadPolicy;
51 import org.apache.maven.archiva.policies.ProxyDownloadException;
52 import org.apache.maven.archiva.policies.urlcache.UrlFailureCache;
53 import org.apache.maven.archiva.repository.ContentNotFoundException;
54 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
55 import org.apache.maven.archiva.repository.RemoteRepositoryContent;
56 import org.apache.maven.archiva.repository.RepositoryContentFactory;
57 import org.apache.maven.archiva.repository.RepositoryException;
58 import org.apache.maven.archiva.repository.RepositoryNotFoundException;
59 import org.apache.maven.archiva.repository.layout.LayoutException;
60 import org.apache.maven.archiva.repository.metadata.MetadataTools;
61 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
62 import org.apache.maven.archiva.repository.scanner.RepositoryContentConsumers;
63 import org.apache.maven.wagon.ConnectionException;
64 import org.apache.maven.wagon.ResourceDoesNotExistException;
65 import org.apache.maven.wagon.Wagon;
66 import org.apache.maven.wagon.WagonException;
67 import org.apache.maven.wagon.authentication.AuthenticationException;
68 import org.apache.maven.wagon.authentication.AuthenticationInfo;
69 import org.apache.maven.wagon.proxy.ProxyInfo;
70 import org.apache.maven.wagon.repository.Repository;
71 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
72 import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
73 import org.codehaus.plexus.registry.Registry;
74 import org.codehaus.plexus.registry.RegistryListener;
75 import org.codehaus.plexus.util.SelectorUtils;
76 import org.slf4j.Logger;
77 import org.slf4j.LoggerFactory;
80 * DefaultRepositoryProxyConnectors
82 * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
84 * @todo exception handling needs work - "not modified" is not really an exceptional case, and it has more layers than your average brown onion
85 * @plexus.component role-hint="default"
87 public class DefaultRepositoryProxyConnectors
88 implements RepositoryProxyConnectors, RegistryListener, Initializable
90 private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class );
95 private ArchivaConfiguration archivaConfiguration;
100 private RepositoryContentFactory repositoryFactory;
103 * @plexus.requirement
105 private MetadataTools metadataTools;
108 * @plexus.requirement role="org.apache.maven.archiva.policies.PreDownloadPolicy"
110 private Map<String, PreDownloadPolicy> preDownloadPolicies;
113 * @plexus.requirement role="org.apache.maven.archiva.policies.PostDownloadPolicy"
115 private Map<String, PostDownloadPolicy> postDownloadPolicies;
118 * @plexus.requirement role="org.apache.maven.archiva.policies.DownloadErrorPolicy"
120 private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
123 * @plexus.requirement role-hint="default"
125 private UrlFailureCache urlFailureCache;
127 private Map<String, List<ProxyConnector>> proxyConnectorMap = new HashMap<String, List<ProxyConnector>>();
129 private Map<String, ProxyInfo> networkProxyMap = new HashMap<String, ProxyInfo>();
132 * @plexus.requirement
134 private RepositoryContentConsumers consumers;
137 * @plexus.requirement
139 private WagonFactory wagonFactory;
141 public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
142 throws ProxyDownloadException
144 File localFile = toLocalFile( repository, artifact );
146 Properties requestProperties = new Properties();
147 requestProperties.setProperty( "filetype", "artifact" );
148 requestProperties.setProperty( "version", artifact.getVersion() );
149 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
151 List<ProxyConnector> connectors = getProxyConnectors( repository );
152 Map<String, Exception> previousExceptions = new LinkedHashMap<String, Exception>();
153 for ( ProxyConnector connector : connectors )
155 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
156 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
158 String targetPath = targetRepository.toPath( artifact );
162 File downloadedFile =
163 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
166 if ( fileExists( downloadedFile ) )
168 log.debug( "Successfully transferred: " + downloadedFile.getAbsolutePath() );
169 return downloadedFile;
172 catch ( NotFoundException e )
174 log.debug( "Artifact " + Keys.toKey( artifact ) + " not found on repository \""
175 + targetRepository.getRepository().getId() + "\"." );
177 catch ( NotModifiedException e )
179 log.debug( "Artifact " + Keys.toKey( artifact ) + " not updated on repository \""
180 + targetRepository.getRepository().getId() + "\"." );
182 catch ( ProxyException e )
184 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
185 targetRepository, localFile, e, previousExceptions );
189 if ( !previousExceptions.isEmpty() )
191 throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories",
192 previousExceptions );
195 log.debug( "Exhausted all target repositories, artifact " + Keys.toKey( artifact ) + " not found." );
200 public File fetchFromProxies( ManagedRepositoryContent repository, String path )
202 File localFile = new File( repository.getRepoRoot(), path );
204 // no update policies for these paths
205 if ( localFile.exists() )
210 Properties requestProperties = new Properties();
211 requestProperties.setProperty( "filetype", "resource" );
212 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
214 List<ProxyConnector> connectors = getProxyConnectors( repository );
215 for ( ProxyConnector connector : connectors )
217 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
218 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
220 String targetPath = path;
224 File downloadedFile =
225 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
228 if ( fileExists( downloadedFile ) )
230 log.debug( "Successfully transferred: " + downloadedFile.getAbsolutePath() );
231 return downloadedFile;
234 catch ( NotFoundException e )
236 log.debug( "Resource " + path + " not found on repository \""
237 + targetRepository.getRepository().getId() + "\"." );
239 catch ( NotModifiedException e )
241 log.debug( "Resource " + path + " not updated on repository \""
242 + targetRepository.getRepository().getId() + "\"." );
244 catch ( ProxyException e )
246 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId()
247 + "\" for resource " + path + ", continuing to next repository. Error message: " + e.getMessage() );
248 log.debug( "Full stack trace", e );
252 log.debug( "Exhausted all target repositories, resource " + path + " not found." );
257 public File fetchFromProxies( ManagedRepositoryContent repository, VersionedReference metadata )
259 File localFile = toLocalFile( repository, metadata );
261 Properties requestProperties = new Properties();
262 requestProperties.setProperty( "filetype", "metadata" );
263 boolean metadataNeedsUpdating = false;
264 long originalTimestamp = getLastModified( localFile );
266 List<ProxyConnector> connectors = getProxyConnectors( repository );
267 for ( ProxyConnector connector : connectors )
269 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
270 String targetPath = metadataTools.toPath( metadata );
272 File localRepoFile = toLocalRepoFile( repository, targetRepository, targetPath );
273 long originalMetadataTimestamp = getLastModified( localRepoFile );
277 transferFile( connector, targetRepository, targetPath, repository, localRepoFile, requestProperties,
280 if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
282 metadataNeedsUpdating = true;
285 catch ( NotFoundException e )
287 log.debug( "Versioned Metadata " + Keys.toKey( metadata )
288 + " not found on remote repository \""
289 + targetRepository.getRepository().getId() + "\"." );
291 catch ( NotModifiedException e )
293 log.debug( "Versioned Metadata " + Keys.toKey( metadata )
294 + " not updated on remote repository \""
295 + targetRepository.getRepository().getId() + "\"." );
297 catch ( ProxyException e )
299 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId() +
300 "\" for versioned Metadata " + Keys.toKey( metadata ) +
301 ", continuing to next repository. Error message: " + e.getMessage() );
302 log.debug( "Full stack trace", e );
306 if ( hasBeenUpdated( localFile, originalTimestamp ) )
308 metadataNeedsUpdating = true;
311 if ( metadataNeedsUpdating )
315 metadataTools.updateMetadata( repository, metadata );
317 catch ( LayoutException e )
319 log.warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage() );
320 // TODO: add into repository report?
322 catch ( RepositoryMetadataException e )
325 .warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
326 // TODO: add into repository report?
328 catch ( IOException e )
331 .warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
332 // TODO: add into repository report?
334 catch ( ContentNotFoundException e )
337 .warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
338 // TODO: add into repository report?
342 if ( fileExists( localFile ) )
350 private long getLastModified( File file )
352 if ( !file.exists() || !file.isFile() )
357 return file.lastModified();
360 private boolean hasBeenUpdated( File file, long originalLastModified )
362 if ( !file.exists() || !file.isFile() )
367 long currentLastModified = getLastModified( file );
368 return ( currentLastModified > originalLastModified );
371 public File fetchFromProxies( ManagedRepositoryContent repository, ProjectReference metadata )
373 File localFile = toLocalFile( repository, metadata );
375 Properties requestProperties = new Properties();
376 requestProperties.setProperty( "filetype", "metadata" );
377 boolean metadataNeedsUpdating = false;
378 long originalTimestamp = getLastModified( localFile );
380 List<ProxyConnector> connectors = getProxyConnectors( repository );
381 for ( ProxyConnector connector : connectors )
383 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
384 String targetPath = metadataTools.toPath( metadata );
386 File localRepoFile = toLocalRepoFile( repository, targetRepository, targetPath );
387 long originalMetadataTimestamp = getLastModified( localRepoFile );
390 transferFile( connector, targetRepository, targetPath, repository, localRepoFile, requestProperties,
393 if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
395 metadataNeedsUpdating = true;
398 catch ( NotFoundException e )
400 log.debug( "Project Metadata " + Keys.toKey( metadata ) + " not found on remote repository \""
401 + targetRepository.getRepository().getId() + "\"." );
403 catch ( NotModifiedException e )
405 log.debug( "Project Metadata " + Keys.toKey( metadata )
406 + " not updated on remote repository \""
407 + targetRepository.getRepository().getId() + "\"." );
409 catch ( ProxyException e )
411 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId() +
412 "\" for project metadata " + Keys.toKey( metadata ) +
413 ", continuing to next repository. Error message: " + e.getMessage() );
414 log.debug( "Full stack trace", e );
419 if ( hasBeenUpdated( localFile, originalTimestamp ) )
421 metadataNeedsUpdating = true;
424 if ( metadataNeedsUpdating )
428 metadataTools.updateMetadata( repository, metadata );
430 catch ( LayoutException e )
432 log.warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage() );
433 // TODO: add into repository report?
435 catch ( RepositoryMetadataException e )
438 .warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
439 // TODO: add into repository report?
441 catch ( IOException e )
444 .warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
445 // TODO: add into repository report?
447 catch ( ContentNotFoundException e )
450 .warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
451 // TODO: add into repository report?
455 if ( fileExists( localFile ) )
463 private File toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository,
466 String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath );
467 return new File( repository.getRepoRoot(), repoPath );
471 * Test if the provided ManagedRepositoryContent has any proxies configured for it.
473 public boolean hasProxies( ManagedRepositoryContent repository )
475 synchronized ( this.proxyConnectorMap )
477 return this.proxyConnectorMap.containsKey( repository.getId() );
481 private File toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact )
483 return repository.toFile( artifact );
486 private File toLocalFile( ManagedRepositoryContent repository, ProjectReference metadata )
488 String sourcePath = metadataTools.toPath( metadata );
489 return new File( repository.getRepoRoot(), sourcePath );
492 private File toLocalFile( ManagedRepositoryContent repository, VersionedReference metadata )
494 String sourcePath = metadataTools.toPath( metadata );
495 return new File( repository.getRepoRoot(), sourcePath );
499 * Simple method to test if the file exists on the local disk.
501 * @param file the file to test. (may be null)
502 * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File.
504 private boolean fileExists( File file )
511 if ( !file.exists() )
516 if ( !file.isFile() )
525 * Perform the transfer of the file.
527 * @param connector the connector configuration to use.
528 * @param remoteRepository the remote repository get the resource from.
529 * @param remotePath the path in the remote repository to the resource to get.
530 * @param repository the managed repository that will hold the file
531 * @param localFile the local file to place the downloaded resource into
532 * @param requestProperties the request properties to utilize for policy handling.
533 * @param executeConsumers whether to execute the consumers after proxying
534 * @return the local file that was downloaded, or null if not downloaded.
535 * @throws NotFoundException if the file was not found on the remote repository.
536 * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository,
537 * but the remote resource is not newer than the local File.
538 * @throws ProxyException if transfer was unsuccessful.
540 private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath,
541 ManagedRepositoryContent repository, File localFile, Properties requestProperties,
542 boolean executeConsumers )
543 throws ProxyException, NotModifiedException
545 String url = remoteRepository.getURL().getUrl();
546 if ( !url.endsWith( "/" ) )
550 url = url + remotePath;
551 requestProperties.setProperty( "url", url );
553 // Is a whitelist defined?
554 if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
556 // Path must belong to whitelist.
557 if ( !matchesPattern( remotePath, connector.getWhitelist() ) )
559 log.debug( "Path [" + remotePath +
560 "] is not part of defined whitelist (skipping transfer from repository [" +
561 remoteRepository.getRepository().getName() + "])." );
566 // Is target path part of blacklist?
567 if ( matchesPattern( remotePath, connector.getBlacklist() ) )
569 log.debug( "Path [" + remotePath + "] is part of blacklist (skipping transfer from repository [" +
570 remoteRepository.getRepository().getName() + "])." );
574 // Handle pre-download policy
577 validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, localFile );
579 catch ( PolicyViolationException e )
581 String emsg = "Transfer not attempted on " + url + " : " + e.getMessage();
582 if ( fileExists( localFile ) )
584 log.info( emsg + ": using already present local file." );
592 // MRM-631 - the lightweight wagon does not reset these - remove if we switch to httpclient based wagon
593 String previousHttpProxyHost = System.getProperty( "http.proxyHost" );
594 String previousHttpProxyPort = System.getProperty( "http.proxyPort" );
595 String previousProxyExclusions = System.getProperty( "http.nonProxyHosts" );
600 RepositoryURL repoUrl = remoteRepository.getURL();
601 String protocol = repoUrl.getProtocol();
602 wagon = (Wagon) wagonFactory.getWagon( "wagon#" + protocol );
605 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
608 boolean connected = connectToRepository( connector, wagon, remoteRepository );
611 localFile = transferSimpleFile( wagon, remoteRepository, remotePath, repository, localFile );
613 // TODO: these should be used to validate the download based on the policies, not always downloaded to
614 // save on connections since md5 is rarely used
615 transferChecksum( wagon, remoteRepository, remotePath, repository, localFile, ".sha1" );
616 transferChecksum( wagon, remoteRepository, remotePath, repository, localFile, ".md5" );
619 catch ( NotFoundException e )
621 urlFailureCache.cacheFailure( url );
624 catch ( NotModifiedException e )
626 // Do not cache url here.
629 catch ( ProxyException e )
631 urlFailureCache.cacheFailure( url );
642 // MRM-631 - the lightweight wagon does not reset these - remove if we switch to httpclient based wagon
643 if ( previousHttpProxyHost != null )
645 System.setProperty( "http.proxyHost", previousHttpProxyHost );
649 System.getProperties().remove( "http.proxyHost" );
651 if ( previousHttpProxyPort != null )
653 System.setProperty( "http.proxyPort", previousHttpProxyPort );
657 System.getProperties().remove( "http.proxyPort" );
659 if ( previousProxyExclusions != null )
661 System.setProperty( "http.nonProxyHosts", previousProxyExclusions );
665 System.getProperties().remove( "http.nonProxyHosts" );
668 catch ( ConnectionException e )
670 log.warn( "Unable to disconnect wagon.", e );
675 // Handle post-download policies.
678 validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, localFile );
680 catch ( PolicyViolationException e )
682 log.info( "Transfer invalidated from " + url + " : " + e.getMessage() );
683 if ( fileExists( localFile ) )
691 if ( executeConsumers )
693 // Just-in-time update of the index and database by executing the consumers for this artifact
694 consumers.executeConsumers( connector.getSourceRepository().getRepository(), localFile );
697 // Everything passes.
703 * Quietly transfer the checksum file from the remote repository to the local file.
706 * @param wagon the wagon instance (should already be connected) to use.
707 * @param remoteRepository the remote repository to transfer from.
708 * @param remotePath the remote path to the resource to get.
709 * @param repository the managed repository that will hold the file
710 * @param localFile the local file that should contain the downloaded contents
711 * @param type the type of checksum to transfer (example: ".md5" or ".sha1")
712 * @throws ProxyException if copying the downloaded file into place did not succeed.
714 private void transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
715 ManagedRepositoryContent repository, File localFile, String type )
716 throws ProxyException
718 String url = remoteRepository.getURL().getUrl() + remotePath;
720 // Transfer checksum does not use the policy.
721 if ( urlFailureCache.hasFailedBefore( url + type ) )
728 File hashFile = new File( localFile.getAbsolutePath() + type );
729 transferSimpleFile( wagon, remoteRepository, remotePath + type, repository, hashFile );
730 log.debug( "Checksum" + type + " Downloaded: " + hashFile );
732 catch ( NotFoundException e )
734 urlFailureCache.cacheFailure( url + type );
735 log.debug( "Transfer failed, checksum not found: " + url );
736 // Consume it, do not pass this on.
738 catch ( NotModifiedException e )
740 log.debug( "Transfer skipped, checksum not modified: " + url );
741 // Consume it, do not pass this on.
743 catch ( ProxyException e )
745 urlFailureCache.cacheFailure( url + type );
746 log.warn( "Transfer failed on checksum: " + url + " : " + e.getMessage(), e );
747 // Critical issue, pass it on.
753 * Perform the transfer of the remote file to the local file specified.
755 * @param wagon the wagon instance to use.
756 * @param remoteRepository the remote repository to use
757 * @param remotePath the remote path to attempt to get
758 * @param repository the managed repository that will hold the file
759 * @param localFile the local file to save to
760 * @return The local file that was transfered.
761 * @throws ProxyException if there was a problem moving the downloaded file into place.
762 * @throws WagonException if there was a problem tranfering the file.
764 private File transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
765 ManagedRepositoryContent repository, File localFile )
766 throws ProxyException
768 assert ( remotePath != null );
770 // Transfer the file.
775 temp = File.createTempFile(localFile.getName() + ".", null, new File( repository.getRepoRoot() ));
777 boolean success = false;
779 if ( !localFile.exists() )
781 log.debug( "Retrieving " + remotePath + " from " + remoteRepository.getRepository().getName() );
782 wagon.get( remotePath, temp );
787 moveTempToTarget( temp, localFile );
790 // You wouldn't get here on failure, a WagonException would have been thrown.
791 log.debug( "Downloaded successfully." );
795 log.debug( "Retrieving " + remotePath + " from " + remoteRepository.getRepository().getName()
797 success = wagon.getIfNewer( remotePath, temp, localFile.lastModified() );
800 throw new NotModifiedException(
801 "Not downloaded, as local file is newer than remote side: " + localFile.getAbsolutePath() );
806 log.debug( "Downloaded successfully." );
807 moveTempToTarget( temp, localFile );
813 catch (IOException e)
815 throw new ProxyException("Could not create temporary file at " + localFile.getAbsolutePath(), e);
817 catch ( ResourceDoesNotExistException e )
819 throw new NotFoundException(
820 "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(),
823 catch ( WagonException e )
825 // TODO: shouldn't have to drill into the cause, but TransferFailedException is often not descriptive enough
828 "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage();
829 if ( e.getCause() != null )
831 msg += " (cause: " + e.getCause() + ")";
833 throw new ProxyException( msg, e );
837 FileUtils.deleteQuietly(temp);
842 * Apply the policies.
844 * @param policies the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
845 * @param settings the map of settings for the policies to execute. (Map of String policy keys, to String policy setting)
846 * @param request the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String,Properties,File)})
847 * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String,Properties,File)})
849 private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings,
850 Properties request, File localFile )
851 throws PolicyViolationException
853 for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
855 String key = entry.getKey();
856 DownloadPolicy policy = entry.getValue();
857 String defaultSetting = policy.getDefaultOption();
858 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
860 log.debug( "Applying [" + key + "] policy with [" + setting + "]" );
863 policy.applyPolicy( setting, request, localFile );
865 catch ( PolicyConfigurationException e )
867 log.error( e.getMessage(), e );
872 private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings,
873 Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
874 File localFile, ProxyException exception, Map<String, Exception> previousExceptions )
875 throws ProxyDownloadException
877 boolean process = true;
878 for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
880 String key = entry.getKey();
881 DownloadErrorPolicy policy = entry.getValue();
882 String defaultSetting = policy.getDefaultOption();
883 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
885 log.debug( "Applying [" + key + "] policy with [" + setting + "]" );
888 // all policies must approve the exception, any can cancel
889 process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions );
895 catch ( PolicyConfigurationException e )
897 log.error( e.getMessage(), e );
903 // if the exception was queued, don't throw it
904 if ( !previousExceptions.containsKey( content.getId() ) )
906 throw new ProxyDownloadException(
907 "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
908 content.getId(), exception );
913 // if the exception was queued, but cancelled, remove it
914 previousExceptions.remove( content.getId() );
917 log.warn( "Transfer error from repository \"" + content.getRepository().getId() + "\" for artifact " +
918 Keys.toKey( artifact ) + ", continuing to next repository. Error message: " + exception.getMessage() );
919 log.debug( "Full stack trace", exception );
923 * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles
924 * its downloaded files.
926 * @param temp The completed download file
927 * @param target The final location of the downloaded file
928 * @throws ProxyException when the temp file cannot replace the target file
930 private void moveTempToTarget( File temp, File target )
931 throws ProxyException
933 if ( target.exists() && !target.delete() )
935 throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() );
938 target.getParentFile().mkdirs();
939 if ( !temp.renameTo( target ) )
941 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
945 FileUtils.copyFile( temp, target );
947 catch ( IOException e )
951 log.debug("Tried to copy file " + temp.getName() + " to " + target.getAbsolutePath() + " but file with this name already exists.");
955 throw new ProxyException( "Cannot copy tmp file " + temp.getAbsolutePath() + " to its final location", e );
960 FileUtils.deleteQuietly(temp);
966 * Using wagon, connect to the remote repository.
968 * @param connector the connector configuration to utilize (for obtaining network proxy configuration from)
969 * @param wagon the wagon instance to establish the connection on.
970 * @param remoteRepository the remote repository to connect to.
971 * @return true if the connection was successful. false if not connected.
973 private boolean connectToRepository( ProxyConnector connector, Wagon wagon,
974 RemoteRepositoryContent remoteRepository )
976 boolean connected = false;
978 final ProxyInfo networkProxy;
979 synchronized ( this.networkProxyMap )
981 networkProxy = (ProxyInfo) this.networkProxyMap.get( connector.getProxyId() );
986 AuthenticationInfo authInfo = null;
987 String username = remoteRepository.getRepository().getUsername();
988 String password = remoteRepository.getRepository().getPassword();
990 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
992 log.debug( "Using username " + username + " to connect to remote repository "
993 + remoteRepository.getURL() );
994 authInfo = new AuthenticationInfo();
995 authInfo.setUserName( username );
996 authInfo.setPassword( password );
1000 log.debug( "No authentication for remote repository needed" );
1003 //Convert seconds to milliseconds
1004 int timeoutInMilliseconds = remoteRepository.getRepository().getTimeout() * 1000;
1007 wagon.setTimeout(timeoutInMilliseconds);
1009 Repository wagonRepository = new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
1010 wagon.connect( wagonRepository, authInfo, networkProxy );
1013 catch ( ConnectionException e )
1016 "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1019 catch ( AuthenticationException e )
1022 "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1030 * Tests whitelist and blacklist patterns against path.
1032 * @param path the path to test.
1033 * @param patterns the list of patterns to check.
1034 * @return true if the path matches at least 1 pattern in the provided patterns list.
1036 private boolean matchesPattern( String path, List<String> patterns )
1038 if ( CollectionUtils.isEmpty( patterns ) )
1043 for ( String pattern : patterns )
1045 if ( SelectorUtils.matchPath( pattern, path, false ) )
1055 * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
1057 public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository )
1059 synchronized ( this.proxyConnectorMap )
1061 List<ProxyConnector> ret = (List<ProxyConnector>) this.proxyConnectorMap.get( repository.getId() );
1064 return Collections.emptyList();
1067 Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
1072 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1074 if ( ConfigurationNames.isNetworkProxy( propertyName ) ||
1075 ConfigurationNames.isManagedRepositories( propertyName ) ||
1076 ConfigurationNames.isRemoteRepositories( propertyName ) ||
1077 ConfigurationNames.isProxyConnector( propertyName ) )
1079 initConnectorsAndNetworkProxies();
1083 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1088 @SuppressWarnings("unchecked")
1089 private void initConnectorsAndNetworkProxies()
1091 synchronized ( this.proxyConnectorMap )
1093 ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator();
1094 this.proxyConnectorMap.clear();
1096 List<ProxyConnectorConfiguration> proxyConfigs = archivaConfiguration.getConfiguration()
1097 .getProxyConnectors();
1098 for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs )
1100 String key = proxyConfig.getSourceRepoId();
1104 // Create connector object.
1105 ProxyConnector connector = new ProxyConnector();
1107 connector.setSourceRepository( repositoryFactory.getManagedRepositoryContent( proxyConfig
1108 .getSourceRepoId() ) );
1109 connector.setTargetRepository( repositoryFactory.getRemoteRepositoryContent( proxyConfig
1110 .getTargetRepoId() ) );
1112 connector.setProxyId( proxyConfig.getProxyId() );
1113 connector.setPolicies( proxyConfig.getPolicies() );
1114 connector.setOrder( proxyConfig.getOrder() );
1116 // Copy any blacklist patterns.
1117 List<String> blacklist = new ArrayList<String>();
1118 if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) )
1120 blacklist.addAll( proxyConfig.getBlackListPatterns() );
1122 connector.setBlacklist( blacklist );
1124 // Copy any whitelist patterns.
1125 List<String> whitelist = new ArrayList<String>();
1126 if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) )
1128 whitelist.addAll( proxyConfig.getWhiteListPatterns() );
1130 connector.setWhitelist( whitelist );
1132 // Get other connectors
1133 List<ProxyConnector> connectors = this.proxyConnectorMap.get( key );
1134 if ( connectors == null )
1136 // Create if we are the first.
1137 connectors = new ArrayList<ProxyConnector>();
1140 // Add the connector.
1141 connectors.add( connector );
1143 // Ensure the list is sorted.
1144 Collections.sort( connectors, proxyOrderSorter );
1146 // Set the key to the list of connectors.
1147 this.proxyConnectorMap.put( key, connectors );
1149 catch ( RepositoryNotFoundException e )
1151 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
1153 catch ( RepositoryException e )
1155 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
1161 synchronized ( this.networkProxyMap )
1163 this.networkProxyMap.clear();
1165 List<NetworkProxyConfiguration> networkProxies = archivaConfiguration.getConfiguration().getNetworkProxies();
1166 for ( NetworkProxyConfiguration networkProxyConfig : networkProxies )
1168 String key = networkProxyConfig.getId();
1170 ProxyInfo proxy = new ProxyInfo();
1172 proxy.setType( networkProxyConfig.getProtocol() );
1173 proxy.setHost( networkProxyConfig.getHost() );
1174 proxy.setPort( networkProxyConfig.getPort() );
1175 proxy.setUserName( networkProxyConfig.getUsername() );
1176 proxy.setPassword( networkProxyConfig.getPassword() );
1178 this.networkProxyMap.put( key, proxy );
1183 public void initialize()
1184 throws InitializationException
1186 initConnectorsAndNetworkProxies();
1187 archivaConfiguration.addChangeListener( this );