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.RepositoryURL;
43 import org.apache.maven.archiva.policies.DownloadErrorPolicy;
44 import org.apache.maven.archiva.policies.DownloadPolicy;
45 import org.apache.maven.archiva.policies.PolicyConfigurationException;
46 import org.apache.maven.archiva.policies.PolicyViolationException;
47 import org.apache.maven.archiva.policies.PostDownloadPolicy;
48 import org.apache.maven.archiva.policies.PreDownloadPolicy;
49 import org.apache.maven.archiva.policies.ProxyDownloadException;
50 import org.apache.maven.archiva.policies.urlcache.UrlFailureCache;
51 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
52 import org.apache.maven.archiva.repository.RemoteRepositoryContent;
53 import org.apache.maven.archiva.repository.RepositoryContentFactory;
54 import org.apache.maven.archiva.repository.RepositoryException;
55 import org.apache.maven.archiva.repository.RepositoryNotFoundException;
56 import org.apache.maven.archiva.repository.metadata.MetadataTools;
57 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
58 import org.apache.maven.archiva.repository.scanner.RepositoryContentConsumers;
59 import org.apache.maven.wagon.ConnectionException;
60 import org.apache.maven.wagon.ResourceDoesNotExistException;
61 import org.apache.maven.wagon.Wagon;
62 import org.apache.maven.wagon.WagonException;
63 import org.apache.maven.wagon.authentication.AuthenticationException;
64 import org.apache.maven.wagon.authentication.AuthenticationInfo;
65 import org.apache.maven.wagon.proxy.ProxyInfo;
66 import org.apache.maven.wagon.repository.Repository;
67 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
68 import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
69 import org.codehaus.plexus.registry.Registry;
70 import org.codehaus.plexus.registry.RegistryListener;
71 import org.codehaus.plexus.util.SelectorUtils;
72 import org.slf4j.Logger;
73 import org.slf4j.LoggerFactory;
76 * DefaultRepositoryProxyConnectors
79 * @todo exception handling needs work - "not modified" is not really an exceptional case, and it has more layers than your average brown onion
80 * @plexus.component role-hint="default"
82 public class DefaultRepositoryProxyConnectors
83 implements RepositoryProxyConnectors, RegistryListener, Initializable
85 private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class );
90 private ArchivaConfiguration archivaConfiguration;
95 private RepositoryContentFactory repositoryFactory;
100 private MetadataTools metadataTools;
103 * @plexus.requirement role="org.apache.maven.archiva.policies.PreDownloadPolicy"
105 private Map<String, PreDownloadPolicy> preDownloadPolicies;
108 * @plexus.requirement role="org.apache.maven.archiva.policies.PostDownloadPolicy"
110 private Map<String, PostDownloadPolicy> postDownloadPolicies;
113 * @plexus.requirement role="org.apache.maven.archiva.policies.DownloadErrorPolicy"
115 private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
118 * @plexus.requirement role-hint="default"
120 private UrlFailureCache urlFailureCache;
122 private Map<String, List<ProxyConnector>> proxyConnectorMap = new HashMap<String, List<ProxyConnector>>();
124 private Map<String, ProxyInfo> networkProxyMap = new HashMap<String, ProxyInfo>();
127 * @plexus.requirement
129 private RepositoryContentConsumers consumers;
132 * @plexus.requirement
134 private WagonFactory wagonFactory;
136 public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
137 throws ProxyDownloadException
139 File workingDirectory = createWorkingDirectory(repository);
142 File localFile = toLocalFile( repository, artifact );
144 Properties requestProperties = new Properties();
145 requestProperties.setProperty( "filetype", "artifact" );
146 requestProperties.setProperty( "version", artifact.getVersion() );
147 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
149 List<ProxyConnector> connectors = getProxyConnectors( repository );
150 Map<String, Exception> previousExceptions = new LinkedHashMap<String, Exception>();
151 for ( ProxyConnector connector : connectors )
153 if (connector.isDisabled())
158 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
159 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
161 String targetPath = targetRepository.toPath( artifact );
165 File downloadedFile =
166 transferFile( connector, targetRepository, targetPath, repository, workingDirectory, localFile, requestProperties,
169 if ( fileExists( downloadedFile ) )
171 log.debug( "Successfully transferred: " + downloadedFile.getAbsolutePath() );
172 return downloadedFile;
175 catch ( NotFoundException e )
177 log.debug( "Artifact " + Keys.toKey( artifact ) + " not found on repository \""
178 + targetRepository.getRepository().getId() + "\"." );
180 catch ( NotModifiedException e )
182 log.debug( "Artifact " + Keys.toKey( artifact ) + " not updated on repository \""
183 + targetRepository.getRepository().getId() + "\"." );
185 catch ( ProxyException e )
187 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
188 targetRepository, localFile, e, previousExceptions );
192 if ( !previousExceptions.isEmpty() )
194 throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories",
195 previousExceptions );
198 log.debug( "Exhausted all target repositories, artifact " + Keys.toKey( artifact ) + " not found." );
202 FileUtils.deleteQuietly(workingDirectory);
208 public File fetchFromProxies( ManagedRepositoryContent repository, String path )
210 File workingDir = createWorkingDirectory(repository);
213 File localFile = new File( repository.getRepoRoot(), path );
215 // no update policies for these paths
216 if ( localFile.exists() )
221 Properties requestProperties = new Properties();
222 requestProperties.setProperty( "filetype", "resource" );
223 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
225 List<ProxyConnector> connectors = getProxyConnectors( repository );
226 for ( ProxyConnector connector : connectors )
228 if (connector.isDisabled())
233 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
234 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
236 String targetPath = path;
240 File downloadedFile =
241 transferFile( connector, targetRepository, targetPath, repository, workingDir, localFile, requestProperties, false );
243 if ( fileExists( downloadedFile ) )
245 log.debug( "Successfully transferred: " + downloadedFile.getAbsolutePath() );
246 return downloadedFile;
249 catch ( NotFoundException e )
251 log.debug( "Resource " + path + " not found on repository \""
252 + targetRepository.getRepository().getId() + "\"." );
254 catch ( NotModifiedException e )
256 log.debug( "Resource " + path + " not updated on repository \""
257 + targetRepository.getRepository().getId() + "\"." );
259 catch ( ProxyException e )
261 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId()
262 + "\" for resource " + path + ", continuing to next repository. Error message: " + e.getMessage() );
263 log.debug( "Full stack trace", e );
267 log.debug( "Exhausted all target repositories, resource " + path + " not found." );
271 FileUtils.deleteQuietly(workingDir);
277 public File fetchMetatadaFromProxies(ManagedRepositoryContent repository, String logicalPath)
279 File workingDir = createWorkingDirectory(repository);
282 File localFile = new File(repository.getRepoRoot(), logicalPath);
284 Properties requestProperties = new Properties();
285 requestProperties.setProperty( "filetype", "metadata" );
286 boolean metadataNeedsUpdating = false;
287 long originalTimestamp = getLastModified( localFile );
289 List<ProxyConnector> connectors = getProxyConnectors( repository );
290 for ( ProxyConnector connector : connectors )
292 if (connector.isDisabled())
297 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
299 File localRepoFile = toLocalRepoFile( repository, targetRepository, logicalPath );
300 long originalMetadataTimestamp = getLastModified( localRepoFile );
304 transferFile( connector, targetRepository, logicalPath, repository, workingDir, localRepoFile, requestProperties, true );
306 if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
308 metadataNeedsUpdating = true;
311 catch ( NotFoundException e )
313 log.debug( "Metadata " + logicalPath
314 + " not found on remote repository \""
315 + targetRepository.getRepository().getId() + "\".", e );
317 catch ( NotModifiedException e )
319 log.debug( "Metadata " + logicalPath
320 + " not updated on remote repository \""
321 + targetRepository.getRepository().getId() + "\".", e );
323 catch ( ProxyException e )
325 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId() +
326 "\" for versioned Metadata " + logicalPath +
327 ", continuing to next repository. Error message: " + e.getMessage() );
328 log.debug( "Full stack trace", e );
332 if ( hasBeenUpdated( localFile, originalTimestamp ) )
334 metadataNeedsUpdating = true;
337 if ( metadataNeedsUpdating || !localFile.exists())
341 metadataTools.updateMetadata( repository, logicalPath );
343 catch ( RepositoryMetadataException e )
345 log.warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
349 if ( fileExists( localFile ) )
356 FileUtils.deleteQuietly(workingDir);
362 private long getLastModified( File file )
364 if ( !file.exists() || !file.isFile() )
369 return file.lastModified();
372 private boolean hasBeenUpdated( File file, long originalLastModified )
374 if ( !file.exists() || !file.isFile() )
379 long currentLastModified = getLastModified( file );
380 return ( currentLastModified > originalLastModified );
383 private File toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository,
386 String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath );
387 return new File( repository.getRepoRoot(), repoPath );
391 * Test if the provided ManagedRepositoryContent has any proxies configured for it.
393 public boolean hasProxies( ManagedRepositoryContent repository )
395 synchronized ( this.proxyConnectorMap )
397 return this.proxyConnectorMap.containsKey( repository.getId() );
401 private File toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact )
403 return repository.toFile( artifact );
407 * Simple method to test if the file exists on the local disk.
409 * @param file the file to test. (may be null)
410 * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File.
412 private boolean fileExists( File file )
419 if ( !file.exists() )
424 if ( !file.isFile() )
433 * Perform the transfer of the file.
435 * @param connector the connector configuration to use.
436 * @param remoteRepository the remote repository get the resource from.
437 * @param remotePath the path in the remote repository to the resource to get.
438 * @param repository the managed repository that will hold the file
439 * @param resource the local file to place the downloaded resource into
440 * @param requestProperties the request properties to utilize for policy handling.
441 * @param executeConsumers whether to execute the consumers after proxying
442 * @return the local file that was downloaded, or null if not downloaded.
443 * @throws NotFoundException if the file was not found on the remote repository.
444 * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository,
445 * but the remote resource is not newer than the local File.
446 * @throws ProxyException if transfer was unsuccessful.
448 private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath,
449 ManagedRepositoryContent repository, File workingDirectory, File resource, Properties requestProperties,
450 boolean executeConsumers )
451 throws ProxyException, NotModifiedException
453 String url = remoteRepository.getURL().getUrl();
454 if ( !url.endsWith( "/" ) )
458 url = url + remotePath;
459 requestProperties.setProperty( "url", url );
461 // Is a whitelist defined?
462 if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
464 // Path must belong to whitelist.
465 if ( !matchesPattern( remotePath, connector.getWhitelist() ) )
467 log.debug( "Path [" + remotePath +
468 "] is not part of defined whitelist (skipping transfer from repository [" +
469 remoteRepository.getRepository().getName() + "])." );
474 // Is target path part of blacklist?
475 if ( matchesPattern( remotePath, connector.getBlacklist() ) )
477 log.debug( "Path [" + remotePath + "] is part of blacklist (skipping transfer from repository [" +
478 remoteRepository.getRepository().getName() + "])." );
482 // Handle pre-download policy
485 validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, resource );
487 catch ( PolicyViolationException e )
489 String emsg = "Transfer not attempted on " + url + " : " + e.getMessage();
490 if ( fileExists( resource ) )
492 log.info( emsg + ": using already present local file." );
500 // MRM-631 - the lightweight wagon does not reset these - remove if we switch to httpclient based wagon
501 String previousHttpProxyHost = System.getProperty( "http.proxyHost" );
502 String previousHttpProxyPort = System.getProperty( "http.proxyPort" );
503 String previousProxyExclusions = System.getProperty( "http.nonProxyHosts" );
507 File tmpResource = null;
512 RepositoryURL repoUrl = remoteRepository.getURL();
513 String protocol = repoUrl.getProtocol();
514 wagon = (Wagon) wagonFactory.getWagon( "wagon#" + protocol );
517 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
520 boolean connected = connectToRepository( connector, wagon, remoteRepository );
523 tmpResource = transferSimpleFile( wagon, remoteRepository, remotePath, repository, workingDirectory, resource );
525 // TODO: these should be used to validate the download based on the policies, not always downloaded to
526 // save on connections since md5 is rarely used
527 tmpSha1 = transferChecksum( wagon, remoteRepository, remotePath, repository, workingDirectory, resource, ".sha1" );
528 tmpMd5 = transferChecksum( wagon, remoteRepository, remotePath, repository, workingDirectory, resource, ".md5" );
531 catch ( NotFoundException e )
533 urlFailureCache.cacheFailure( url );
536 catch ( NotModifiedException e )
538 // Do not cache url here.
541 catch ( ProxyException e )
543 urlFailureCache.cacheFailure( url );
554 // MRM-631 - the lightweight wagon does not reset these - remove if we switch to httpclient based wagon
555 if ( previousHttpProxyHost != null )
557 System.setProperty( "http.proxyHost", previousHttpProxyHost );
561 System.getProperties().remove( "http.proxyHost" );
563 if ( previousHttpProxyPort != null )
565 System.setProperty( "http.proxyPort", previousHttpProxyPort );
569 System.getProperties().remove( "http.proxyPort" );
571 if ( previousProxyExclusions != null )
573 System.setProperty( "http.nonProxyHosts", previousProxyExclusions );
577 System.getProperties().remove( "http.nonProxyHosts" );
580 catch ( ConnectionException e )
582 log.warn( "Unable to disconnect wagon.", e );
587 // Handle post-download policies.
590 validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, tmpResource );
592 catch ( PolicyViolationException e )
594 log.info( "Transfer invalidated from " + url + " : " + e.getMessage() );
595 executeConsumers = false;
596 if ( !fileExists( tmpResource ) )
602 if (resource != null)
604 synchronized (resource.getAbsolutePath().intern())
606 File directory = resource.getParentFile();
607 moveFileIfExists(tmpMd5, directory);
608 moveFileIfExists(tmpSha1, directory);
609 moveFileIfExists(tmpResource, directory);
613 if ( executeConsumers )
615 // Just-in-time update of the index and database by executing the consumers for this artifact
616 consumers.executeConsumers( connector.getSourceRepository().getRepository(), resource );
625 * Moves the file into repository location if it exists
627 * @param fileToMove this could be either the main artifact, sha1 or md5 checksum file.
628 * @param directory directory to write files to
630 private void moveFileIfExists(File fileToMove, File directory) throws ProxyException
632 if (fileToMove != null && fileToMove.exists())
634 File newLocation = new File(directory, fileToMove.getName());
635 moveTempToTarget(fileToMove, newLocation);
641 * Quietly transfer the checksum file from the remote repository to the local file.
644 * @param wagon the wagon instance (should already be connected) to use.
645 * @param remoteRepository the remote repository to transfer from.
646 * @param remotePath the remote path to the resource to get.
647 * @param repository the managed repository that will hold the file
648 * @param localFile the local file that should contain the downloaded contents
649 * @param type the type of checksum to transfer (example: ".md5" or ".sha1")
650 * @throws ProxyException if copying the downloaded file into place did not succeed.
652 private File transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
653 ManagedRepositoryContent repository, File workingDirectory, File localFile, String type )
654 throws ProxyException
656 File hashFile = new File( localFile.getAbsolutePath() + type );
657 File tmpChecksum = new File(workingDirectory, hashFile.getName());
658 String url = remoteRepository.getURL().getUrl() + remotePath;
660 // Transfer checksum does not use the policy.
661 if ( urlFailureCache.hasFailedBefore( url + type ) )
668 transferSimpleFile( wagon, remoteRepository, remotePath + type, repository, workingDirectory, hashFile );
669 log.debug( "Checksum" + type + " Downloaded: " + hashFile );
671 catch ( NotFoundException e )
673 urlFailureCache.cacheFailure( url + type );
674 log.debug( "Transfer failed, checksum not found: " + url );
675 // Consume it, do not pass this on.
677 catch ( NotModifiedException e )
679 log.debug( "Transfer skipped, checksum not modified: " + url );
680 // Consume it, do not pass this on.
682 catch ( ProxyException e )
684 urlFailureCache.cacheFailure( url + type );
685 log.warn( "Transfer failed on checksum: " + url + " : " + e.getMessage(), e );
686 // Critical issue, pass it on.
693 * Perform the transfer of the remote file to the local file specified.
695 * @param wagon the wagon instance to use.
696 * @param remoteRepository the remote repository to use
697 * @param remotePath the remote path to attempt to get
698 * @param repository the managed repository that will hold the file
699 * @param localFile the local file to save to
700 * @return The local file that was transfered.
701 * @throws ProxyException if there was a problem moving the downloaded file into place.
702 * @throws WagonException if there was a problem tranfering the file.
704 private File transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
705 ManagedRepositoryContent repository, File workingDirectory, File localFile )
706 throws ProxyException
708 assert ( remotePath != null );
710 // Transfer the file.
715 temp = new File(workingDirectory, localFile.getName());
717 boolean success = false;
719 if ( !localFile.exists() )
721 log.debug( "Retrieving " + remotePath + " from " + remoteRepository.getRepository().getName() );
722 wagon.get( remotePath, temp );
725 // You wouldn't get here on failure, a WagonException would have been thrown.
726 log.debug( "Downloaded successfully." );
730 log.debug( "Retrieving " + remotePath + " from " + remoteRepository.getRepository().getName()
732 success = wagon.getIfNewer( remotePath, temp, localFile.lastModified() );
735 throw new NotModifiedException(
736 "Not downloaded, as local file is newer than remote side: " + localFile.getAbsolutePath() );
741 log.debug( "Downloaded successfully." );
747 catch ( ResourceDoesNotExistException e )
749 throw new NotFoundException(
750 "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(),
753 catch ( WagonException e )
755 // TODO: shouldn't have to drill into the cause, but TransferFailedException is often not descriptive enough
758 "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage();
759 if ( e.getCause() != null )
761 msg += " (cause: " + e.getCause() + ")";
763 throw new ProxyException( msg, e );
768 * Apply the policies.
770 * @param policies the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
771 * @param settings the map of settings for the policies to execute. (Map of String policy keys, to String policy setting)
772 * @param request the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String,Properties,File)})
773 * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String,Properties,File)})
775 private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings,
776 Properties request, File localFile )
777 throws PolicyViolationException
779 for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
781 String key = entry.getKey();
782 DownloadPolicy policy = entry.getValue();
783 String defaultSetting = policy.getDefaultOption();
784 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
786 log.debug( "Applying [" + key + "] policy with [" + setting + "]" );
789 policy.applyPolicy( setting, request, localFile );
791 catch ( PolicyConfigurationException e )
793 log.error( e.getMessage(), e );
798 private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings,
799 Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
800 File localFile, ProxyException exception, Map<String, Exception> previousExceptions )
801 throws ProxyDownloadException
803 boolean process = true;
804 for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
806 String key = entry.getKey();
807 DownloadErrorPolicy policy = entry.getValue();
808 String defaultSetting = policy.getDefaultOption();
809 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
811 log.debug( "Applying [" + key + "] policy with [" + setting + "]" );
814 // all policies must approve the exception, any can cancel
815 process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions );
821 catch ( PolicyConfigurationException e )
823 log.error( e.getMessage(), e );
829 // if the exception was queued, don't throw it
830 if ( !previousExceptions.containsKey( content.getId() ) )
832 throw new ProxyDownloadException(
833 "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
834 content.getId(), exception );
839 // if the exception was queued, but cancelled, remove it
840 previousExceptions.remove( content.getId() );
843 log.warn( "Transfer error from repository \"" + content.getRepository().getId() + "\" for artifact " +
844 Keys.toKey( artifact ) + ", continuing to next repository. Error message: " + exception.getMessage() );
845 log.debug( "Full stack trace", exception );
849 * Creates a working directory in the repository root for this request
851 * @return file location of working directory
853 private File createWorkingDirectory(ManagedRepositoryContent repository)
855 //TODO: This is ugly - lets actually clean this up when we get the new repository api
858 File tmpDir = File.createTempFile(".workingdirectory", null, new File(repository.getRepoRoot()));
863 catch (IOException e)
865 throw new RuntimeException("Could not create working directory for this request", e);
870 * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles
871 * its downloaded files.
873 * @param temp The completed download file
874 * @param target The final location of the downloaded file
875 * @throws ProxyException when the temp file cannot replace the target file
877 private void moveTempToTarget( File temp, File target )
878 throws ProxyException
880 if ( target.exists() && !target.delete() )
882 throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() );
885 target.getParentFile().mkdirs();
886 if ( !temp.renameTo( target ) )
888 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
892 FileUtils.copyFile( temp, target );
894 catch ( IOException e )
898 log.debug("Tried to copy file " + temp.getName() + " to " + target.getAbsolutePath() + " but file with this name already exists.");
902 throw new ProxyException( "Cannot copy tmp file " + temp.getAbsolutePath() + " to its final location", e );
907 FileUtils.deleteQuietly(temp);
913 * Using wagon, connect to the remote repository.
915 * @param connector the connector configuration to utilize (for obtaining network proxy configuration from)
916 * @param wagon the wagon instance to establish the connection on.
917 * @param remoteRepository the remote repository to connect to.
918 * @return true if the connection was successful. false if not connected.
920 private boolean connectToRepository( ProxyConnector connector, Wagon wagon,
921 RemoteRepositoryContent remoteRepository )
923 boolean connected = false;
925 final ProxyInfo networkProxy;
926 synchronized ( this.networkProxyMap )
928 networkProxy = (ProxyInfo) this.networkProxyMap.get( connector.getProxyId() );
931 if ( log.isDebugEnabled() )
933 if ( networkProxy != null )
935 // TODO: move to proxyInfo.toString()
937 "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
938 + " to connect to remote repository " + remoteRepository.getURL();
939 if ( networkProxy.getNonProxyHosts() != null )
941 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
943 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
945 msg += "; as user: " + networkProxy.getUserName();
951 AuthenticationInfo authInfo = null;
952 String username = remoteRepository.getRepository().getUsername();
953 String password = remoteRepository.getRepository().getPassword();
955 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
957 log.debug( "Using username " + username + " to connect to remote repository "
958 + remoteRepository.getURL() );
959 authInfo = new AuthenticationInfo();
960 authInfo.setUserName( username );
961 authInfo.setPassword( password );
964 //Convert seconds to milliseconds
965 int timeoutInMilliseconds = remoteRepository.getRepository().getTimeout() * 1000;
968 wagon.setTimeout(timeoutInMilliseconds);
972 Repository wagonRepository = new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
973 wagon.connect( wagonRepository, authInfo, networkProxy );
976 catch ( ConnectionException e )
979 "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
982 catch ( AuthenticationException e )
985 "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
993 * Tests whitelist and blacklist patterns against path.
995 * @param path the path to test.
996 * @param patterns the list of patterns to check.
997 * @return true if the path matches at least 1 pattern in the provided patterns list.
999 private boolean matchesPattern( String path, List<String> patterns )
1001 if ( CollectionUtils.isEmpty( patterns ) )
1006 for ( String pattern : patterns )
1008 if ( SelectorUtils.matchPath( pattern, path, false ) )
1018 * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
1020 public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository )
1022 synchronized ( this.proxyConnectorMap )
1024 List<ProxyConnector> ret = (List<ProxyConnector>) this.proxyConnectorMap.get( repository.getId() );
1027 return Collections.emptyList();
1030 Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
1035 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1037 if ( ConfigurationNames.isNetworkProxy( propertyName ) ||
1038 ConfigurationNames.isManagedRepositories( propertyName ) ||
1039 ConfigurationNames.isRemoteRepositories( propertyName ) ||
1040 ConfigurationNames.isProxyConnector( propertyName ) )
1042 initConnectorsAndNetworkProxies();
1046 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1051 @SuppressWarnings("unchecked")
1052 private void initConnectorsAndNetworkProxies()
1054 synchronized ( this.proxyConnectorMap )
1056 ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator();
1057 this.proxyConnectorMap.clear();
1059 List<ProxyConnectorConfiguration> proxyConfigs = archivaConfiguration.getConfiguration()
1060 .getProxyConnectors();
1061 for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs )
1063 String key = proxyConfig.getSourceRepoId();
1067 // Create connector object.
1068 ProxyConnector connector = new ProxyConnector();
1070 connector.setSourceRepository( repositoryFactory.getManagedRepositoryContent( proxyConfig
1071 .getSourceRepoId() ) );
1072 connector.setTargetRepository( repositoryFactory.getRemoteRepositoryContent( proxyConfig
1073 .getTargetRepoId() ) );
1075 connector.setProxyId( proxyConfig.getProxyId() );
1076 connector.setPolicies( proxyConfig.getPolicies() );
1077 connector.setOrder( proxyConfig.getOrder() );
1078 connector.setDisabled( proxyConfig.isDisabled() );
1080 // Copy any blacklist patterns.
1081 List<String> blacklist = new ArrayList<String>();
1082 if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) )
1084 blacklist.addAll( proxyConfig.getBlackListPatterns() );
1086 connector.setBlacklist( blacklist );
1088 // Copy any whitelist patterns.
1089 List<String> whitelist = new ArrayList<String>();
1090 if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) )
1092 whitelist.addAll( proxyConfig.getWhiteListPatterns() );
1094 connector.setWhitelist( whitelist );
1096 // Get other connectors
1097 List<ProxyConnector> connectors = this.proxyConnectorMap.get( key );
1098 if ( connectors == null )
1100 // Create if we are the first.
1101 connectors = new ArrayList<ProxyConnector>();
1104 // Add the connector.
1105 connectors.add( connector );
1107 // Ensure the list is sorted.
1108 Collections.sort( connectors, proxyOrderSorter );
1110 // Set the key to the list of connectors.
1111 this.proxyConnectorMap.put( key, connectors );
1113 catch ( RepositoryNotFoundException e )
1115 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
1117 catch ( RepositoryException e )
1119 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
1125 synchronized ( this.networkProxyMap )
1127 this.networkProxyMap.clear();
1129 List<NetworkProxyConfiguration> networkProxies = archivaConfiguration.getConfiguration().getNetworkProxies();
1130 for ( NetworkProxyConfiguration networkProxyConfig : networkProxies )
1132 String key = networkProxyConfig.getId();
1134 ProxyInfo proxy = new ProxyInfo();
1136 proxy.setType( networkProxyConfig.getProtocol() );
1137 proxy.setHost( networkProxyConfig.getHost() );
1138 proxy.setPort( networkProxyConfig.getPort() );
1139 proxy.setUserName( networkProxyConfig.getUsername() );
1140 proxy.setPassword( networkProxyConfig.getPassword() );
1142 this.networkProxyMap.put( key, proxy );
1147 public void initialize()
1148 throws InitializationException
1150 initConnectorsAndNetworkProxies();
1151 archivaConfiguration.addChangeListener( this );