1 package org.apache.maven.archiva.proxy;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import org.apache.commons.collections.CollectionUtils;
23 import org.apache.commons.io.FileUtils;
24 import org.apache.commons.lang.StringUtils;
25 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
26 import org.apache.maven.archiva.configuration.ConfigurationNames;
27 import org.apache.maven.archiva.configuration.NetworkProxyConfiguration;
28 import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration;
29 import org.apache.maven.archiva.model.ArtifactReference;
30 import org.apache.maven.archiva.model.Keys;
31 import org.apache.maven.archiva.model.ProjectReference;
32 import org.apache.maven.archiva.model.RepositoryURL;
33 import org.apache.maven.archiva.model.VersionedReference;
34 import org.apache.maven.archiva.policies.DownloadErrorPolicy;
35 import org.apache.maven.archiva.policies.DownloadPolicy;
36 import org.apache.maven.archiva.policies.PolicyConfigurationException;
37 import org.apache.maven.archiva.policies.PolicyViolationException;
38 import org.apache.maven.archiva.policies.PostDownloadPolicy;
39 import org.apache.maven.archiva.policies.PreDownloadPolicy;
40 import org.apache.maven.archiva.policies.ProxyDownloadException;
41 import org.apache.maven.archiva.policies.urlcache.UrlFailureCache;
42 import org.apache.maven.archiva.repository.ContentNotFoundException;
43 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
44 import org.apache.maven.archiva.repository.RemoteRepositoryContent;
45 import org.apache.maven.archiva.repository.RepositoryContentFactory;
46 import org.apache.maven.archiva.repository.RepositoryException;
47 import org.apache.maven.archiva.repository.RepositoryNotFoundException;
48 import org.apache.maven.archiva.repository.layout.LayoutException;
49 import org.apache.maven.archiva.repository.metadata.MetadataTools;
50 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
51 import org.apache.maven.archiva.repository.scanner.RepositoryContentConsumers;
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.personality.plexus.lifecycle.phase.Initializable;
61 import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
62 import org.codehaus.plexus.registry.Registry;
63 import org.codehaus.plexus.registry.RegistryListener;
64 import org.codehaus.plexus.util.SelectorUtils;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
69 import java.io.IOException;
70 import java.util.ArrayList;
71 import java.util.Collections;
72 import java.util.HashMap;
73 import java.util.LinkedHashMap;
74 import java.util.List;
76 import java.util.Map.Entry;
77 import java.util.Properties;
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;
98 * @plexus.requirement role="org.apache.maven.wagon.Wagon"
100 private Map<String, Wagon> wagons;
103 * @plexus.requirement
105 private RepositoryContentFactory repositoryFactory;
108 * @plexus.requirement
110 private MetadataTools metadataTools;
113 * @plexus.requirement role="org.apache.maven.archiva.policies.PreDownloadPolicy"
115 private Map<String, PreDownloadPolicy> preDownloadPolicies;
118 * @plexus.requirement role="org.apache.maven.archiva.policies.PostDownloadPolicy"
120 private Map<String, PostDownloadPolicy> postDownloadPolicies;
123 * @plexus.requirement role="org.apache.maven.archiva.policies.DownloadErrorPolicy"
125 private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
128 * @plexus.requirement role-hint="default"
130 private UrlFailureCache urlFailureCache;
132 private Map<String, List<ProxyConnector>> proxyConnectorMap = new HashMap<String, List<ProxyConnector>>();
134 private Map<String, ProxyInfo> networkProxyMap = new HashMap<String, ProxyInfo>();
137 * @plexus.requirement
139 private RepositoryContentConsumers consumers;
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." );
595 RepositoryURL repoUrl = remoteRepository.getURL();
596 String protocol = repoUrl.getProtocol();
597 wagon = (Wagon) wagons.get( protocol );
600 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
603 boolean connected = connectToRepository( connector, wagon, remoteRepository );
606 localFile = transferSimpleFile( wagon, remoteRepository, remotePath, repository, localFile );
608 // TODO: these should be used to validate the download based on the policies, not always downloaded to
609 // save on connections since md5 is rarely used
610 transferChecksum( wagon, remoteRepository, remotePath, repository, localFile, ".sha1" );
611 transferChecksum( wagon, remoteRepository, remotePath, repository, localFile, ".md5" );
614 catch ( NotFoundException e )
616 urlFailureCache.cacheFailure( url );
619 catch ( NotModifiedException e )
621 // Do not cache url here.
624 catch ( ProxyException e )
626 urlFailureCache.cacheFailure( url );
637 catch ( ConnectionException e )
639 log.warn( "Unable to disconnect wagon.", e );
644 // Handle post-download policies.
647 validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, localFile );
649 catch ( PolicyViolationException e )
651 log.info( "Transfer invalidated from " + url + " : " + e.getMessage() );
652 if ( fileExists( localFile ) )
660 if ( executeConsumers )
662 // Just-in-time update of the index and database by executing the consumers for this artifact
663 consumers.executeConsumers( connector.getSourceRepository().getRepository(), localFile );
666 // Everything passes.
672 * Quietly transfer the checksum file from the remote repository to the local file.
675 * @param wagon the wagon instance (should already be connected) to use.
676 * @param remoteRepository the remote repository to transfer from.
677 * @param remotePath the remote path to the resource to get.
678 * @param repository the managed repository that will hold the file
679 * @param localFile the local file that should contain the downloaded contents
680 * @param type the type of checksum to transfer (example: ".md5" or ".sha1")
681 * @throws ProxyException if copying the downloaded file into place did not succeed.
683 private void transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
684 ManagedRepositoryContent repository, File localFile, String type )
685 throws ProxyException
687 String url = remoteRepository.getURL().getUrl() + remotePath;
689 // Transfer checksum does not use the policy.
690 if ( urlFailureCache.hasFailedBefore( url + type ) )
697 File hashFile = new File( localFile.getAbsolutePath() + type );
698 transferSimpleFile( wagon, remoteRepository, remotePath + type, repository, hashFile );
699 log.debug( "Checksum" + type + " Downloaded: " + hashFile );
701 catch ( NotFoundException e )
703 urlFailureCache.cacheFailure( url + type );
704 log.debug( "Transfer failed, checksum not found: " + url );
705 // Consume it, do not pass this on.
707 catch ( NotModifiedException e )
709 log.debug( "Transfer skipped, checksum not modified: " + url );
710 // Consume it, do not pass this on.
712 catch ( ProxyException e )
714 urlFailureCache.cacheFailure( url + type );
715 log.warn( "Transfer failed on checksum: " + url + " : " + e.getMessage(), e );
716 // Critical issue, pass it on.
722 * Perform the transfer of the remote file to the local file specified.
724 * @param wagon the wagon instance to use.
725 * @param remoteRepository the remote repository to use
726 * @param remotePath the remote path to attempt to get
727 * @param repository the managed repository that will hold the file
728 * @param localFile the local file to save to
729 * @return The local file that was transfered.
730 * @throws ProxyException if there was a problem moving the downloaded file into place.
731 * @throws WagonException if there was a problem tranfering the file.
733 private File transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
734 ManagedRepositoryContent repository, File localFile )
735 throws ProxyException
737 assert ( remotePath != null );
739 // Transfer the file.
744 temp = File.createTempFile(localFile.getName() + ".", null, new File( repository.getRepoRoot() ));
746 boolean success = false;
748 if ( !localFile.exists() )
750 log.debug( "Retrieving " + remotePath + " from " + remoteRepository.getRepository().getName() );
751 wagon.get( remotePath, temp );
756 moveTempToTarget( temp, localFile );
759 // You wouldn't get here on failure, a WagonException would have been thrown.
760 log.debug( "Downloaded successfully." );
764 log.debug( "Retrieving " + remotePath + " from " + remoteRepository.getRepository().getName()
766 success = wagon.getIfNewer( remotePath, temp, localFile.lastModified() );
769 throw new NotModifiedException(
770 "Not downloaded, as local file is newer than remote side: " + localFile.getAbsolutePath() );
775 log.debug( "Downloaded successfully." );
776 moveTempToTarget( temp, localFile );
782 catch (IOException e)
784 throw new ProxyException("Could not create temporary file at " + localFile.getAbsolutePath(), e);
786 catch ( ResourceDoesNotExistException e )
788 throw new NotFoundException(
789 "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(),
792 catch ( WagonException e )
794 throw new ProxyException(
795 "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage(),
800 FileUtils.deleteQuietly(temp);
805 * Apply the policies.
807 * @param policies the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
808 * @param settings the map of settings for the policies to execute. (Map of String policy keys, to String policy setting)
809 * @param request the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String,Properties,File)})
810 * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String,Properties,File)})
812 private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings,
813 Properties request, File localFile )
814 throws PolicyViolationException
816 for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
818 String key = entry.getKey();
819 DownloadPolicy policy = entry.getValue();
820 String defaultSetting = policy.getDefaultOption();
821 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
823 log.debug( "Applying [" + key + "] policy with [" + setting + "]" );
826 policy.applyPolicy( setting, request, localFile );
828 catch ( PolicyConfigurationException e )
830 log.error( e.getMessage(), e );
835 private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings,
836 Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
837 File localFile, ProxyException exception, Map<String, Exception> previousExceptions )
838 throws ProxyDownloadException
840 boolean process = true;
841 for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
843 String key = entry.getKey();
844 DownloadErrorPolicy policy = entry.getValue();
845 String defaultSetting = policy.getDefaultOption();
846 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
848 log.debug( "Applying [" + key + "] policy with [" + setting + "]" );
851 // all policies must approve the exception, any can cancel
852 process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions );
858 catch ( PolicyConfigurationException e )
860 log.error( e.getMessage(), e );
866 // if the exception was queued, don't throw it
867 if ( !previousExceptions.containsKey( content.getId() ) )
869 throw new ProxyDownloadException(
870 "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
871 content.getId(), exception );
876 // if the exception was queued, but cancelled, remove it
877 previousExceptions.remove( content.getId() );
880 log.warn( "Transfer error from repository \"" + content.getRepository().getId() + "\" for artifact " +
881 Keys.toKey( artifact ) + ", continuing to next repository. Error message: " + exception.getMessage() );
882 log.debug( "Full stack trace", exception );
886 * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles
887 * its downloaded files.
889 * @param temp The completed download file
890 * @param target The final location of the downloaded file
891 * @throws ProxyException when the temp file cannot replace the target file
893 private void moveTempToTarget( File temp, File target )
894 throws ProxyException
896 if ( target.exists() && !target.delete() )
898 throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() );
901 target.getParentFile().mkdirs();
902 if ( !temp.renameTo( target ) )
904 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
908 FileUtils.copyFile( temp, target );
910 catch ( IOException e )
914 log.debug("Tried to copy file " + temp.getName() + " to " + target.getAbsolutePath() + " but file with this name already exists.");
918 throw new ProxyException( "Cannot copy tmp file " + temp.getAbsolutePath() + " to its final location", e );
923 FileUtils.deleteQuietly(temp);
929 * Using wagon, connect to the remote repository.
931 * @param connector the connector configuration to utilize (for obtaining network proxy configuration from)
932 * @param wagon the wagon instance to establish the connection on.
933 * @param remoteRepository the remote repository to connect to.
934 * @return true if the connection was successful. false if not connected.
936 private boolean connectToRepository( ProxyConnector connector, Wagon wagon,
937 RemoteRepositoryContent remoteRepository )
939 boolean connected = false;
941 ProxyInfo networkProxy = null;
942 synchronized ( this.networkProxyMap )
944 networkProxy = (ProxyInfo) this.networkProxyMap.get( connector.getProxyId() );
949 AuthenticationInfo authInfo = null;
950 String username = remoteRepository.getRepository().getUsername();
951 String password = remoteRepository.getRepository().getPassword();
953 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
955 log.debug( "Using username " + username + " to connect to remote repository "
956 + remoteRepository.getURL() );
957 authInfo = new AuthenticationInfo();
958 authInfo.setUserName( username );
959 authInfo.setPassword( password );
963 log.debug( "No authentication for remote repository needed" );
966 //Convert seconds to milliseconds
967 int timeoutInMilliseconds = remoteRepository.getRepository().getTimeout() * 1000;
970 wagon.setTimeout(timeoutInMilliseconds);
972 Repository wagonRepository = new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
973 if ( networkProxy != null )
975 wagon.connect( wagonRepository, authInfo, networkProxy );
979 wagon.connect( wagonRepository, authInfo );
983 catch ( ConnectionException e )
986 "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
989 catch ( AuthenticationException e )
992 "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
1000 * Tests whitelist and blacklist patterns against path.
1002 * @param path the path to test.
1003 * @param patterns the list of patterns to check.
1004 * @return true if the path matches at least 1 pattern in the provided patterns list.
1006 private boolean matchesPattern( String path, List<String> patterns )
1008 if ( CollectionUtils.isEmpty( patterns ) )
1013 for ( String pattern : patterns )
1015 if ( SelectorUtils.matchPath( pattern, path, false ) )
1025 * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
1027 public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository )
1029 synchronized ( this.proxyConnectorMap )
1031 List<ProxyConnector> ret = (List<ProxyConnector>) this.proxyConnectorMap.get( repository.getId() );
1034 return Collections.emptyList();
1037 Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
1042 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1044 if ( ConfigurationNames.isNetworkProxy( propertyName ) ||
1045 ConfigurationNames.isManagedRepositories( propertyName ) ||
1046 ConfigurationNames.isRemoteRepositories( propertyName ) ||
1047 ConfigurationNames.isProxyConnector( propertyName ) )
1049 initConnectorsAndNetworkProxies();
1053 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1058 @SuppressWarnings("unchecked")
1059 private void initConnectorsAndNetworkProxies()
1061 synchronized ( this.proxyConnectorMap )
1063 ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator();
1064 this.proxyConnectorMap.clear();
1066 List<ProxyConnectorConfiguration> proxyConfigs = archivaConfiguration.getConfiguration()
1067 .getProxyConnectors();
1068 for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs )
1070 String key = proxyConfig.getSourceRepoId();
1074 // Create connector object.
1075 ProxyConnector connector = new ProxyConnector();
1077 connector.setSourceRepository( repositoryFactory.getManagedRepositoryContent( proxyConfig
1078 .getSourceRepoId() ) );
1079 connector.setTargetRepository( repositoryFactory.getRemoteRepositoryContent( proxyConfig
1080 .getTargetRepoId() ) );
1082 connector.setProxyId( proxyConfig.getProxyId() );
1083 connector.setPolicies( proxyConfig.getPolicies() );
1084 connector.setOrder( proxyConfig.getOrder() );
1086 // Copy any blacklist patterns.
1087 List<String> blacklist = new ArrayList<String>();
1088 if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) )
1090 blacklist.addAll( proxyConfig.getBlackListPatterns() );
1092 connector.setBlacklist( blacklist );
1094 // Copy any whitelist patterns.
1095 List<String> whitelist = new ArrayList<String>();
1096 if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) )
1098 whitelist.addAll( proxyConfig.getWhiteListPatterns() );
1100 connector.setWhitelist( whitelist );
1102 // Get other connectors
1103 List<ProxyConnector> connectors = this.proxyConnectorMap.get( key );
1104 if ( connectors == null )
1106 // Create if we are the first.
1107 connectors = new ArrayList<ProxyConnector>();
1110 // Add the connector.
1111 connectors.add( connector );
1113 // Ensure the list is sorted.
1114 Collections.sort( connectors, proxyOrderSorter );
1116 // Set the key to the list of connectors.
1117 this.proxyConnectorMap.put( key, connectors );
1119 catch ( RepositoryNotFoundException e )
1121 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
1123 catch ( RepositoryException e )
1125 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
1131 synchronized ( this.networkProxyMap )
1133 this.networkProxyMap.clear();
1135 List<NetworkProxyConfiguration> networkProxies = archivaConfiguration.getConfiguration().getNetworkProxies();
1136 for ( NetworkProxyConfiguration networkProxyConfig : networkProxies )
1138 String key = networkProxyConfig.getId();
1140 ProxyInfo proxy = new ProxyInfo();
1142 proxy.setType( networkProxyConfig.getProtocol() );
1143 proxy.setHost( networkProxyConfig.getHost() );
1144 proxy.setPort( networkProxyConfig.getPort() );
1145 proxy.setUserName( networkProxyConfig.getUsername() );
1146 proxy.setPassword( networkProxyConfig.getPassword() );
1148 this.networkProxyMap.put( key, proxy );
1153 public void initialize()
1154 throws InitializationException
1156 initConnectorsAndNetworkProxies();
1157 archivaConfiguration.addChangeListener( this );