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
78 * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
80 * @todo exception handling needs work - "not modified" is not really an exceptional case, and it has more layers than your average brown onion
81 * @plexus.component role-hint="default"
83 public class DefaultRepositoryProxyConnectors
84 implements RepositoryProxyConnectors, RegistryListener, Initializable
86 private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class );
91 private ArchivaConfiguration archivaConfiguration;
96 private RepositoryContentFactory repositoryFactory;
101 private MetadataTools metadataTools;
104 * @plexus.requirement role="org.apache.maven.archiva.policies.PreDownloadPolicy"
106 private Map<String, PreDownloadPolicy> preDownloadPolicies;
109 * @plexus.requirement role="org.apache.maven.archiva.policies.PostDownloadPolicy"
111 private Map<String, PostDownloadPolicy> postDownloadPolicies;
114 * @plexus.requirement role="org.apache.maven.archiva.policies.DownloadErrorPolicy"
116 private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
119 * @plexus.requirement role-hint="default"
121 private UrlFailureCache urlFailureCache;
123 private Map<String, List<ProxyConnector>> proxyConnectorMap = new HashMap<String, List<ProxyConnector>>();
125 private Map<String, ProxyInfo> networkProxyMap = new HashMap<String, ProxyInfo>();
128 * @plexus.requirement
130 private RepositoryContentConsumers consumers;
133 * @plexus.requirement
135 private WagonFactory wagonFactory;
137 public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
138 throws ProxyDownloadException
140 File workingDirectory = createWorkingDirectory(repository);
143 File localFile = toLocalFile( repository, artifact );
145 Properties requestProperties = new Properties();
146 requestProperties.setProperty( "filetype", "artifact" );
147 requestProperties.setProperty( "version", artifact.getVersion() );
148 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
150 List<ProxyConnector> connectors = getProxyConnectors( repository );
151 Map<String, Exception> previousExceptions = new LinkedHashMap<String, Exception>();
152 for ( ProxyConnector connector : connectors )
154 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
155 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
157 String targetPath = targetRepository.toPath( artifact );
161 File downloadedFile =
162 transferFile( connector, targetRepository, targetPath, repository, workingDirectory, localFile, requestProperties,
165 if ( fileExists( downloadedFile ) )
167 log.debug( "Successfully transferred: " + downloadedFile.getAbsolutePath() );
168 return downloadedFile;
171 catch ( NotFoundException e )
173 log.debug( "Artifact " + Keys.toKey( artifact ) + " not found on repository \""
174 + targetRepository.getRepository().getId() + "\"." );
176 catch ( NotModifiedException e )
178 log.debug( "Artifact " + Keys.toKey( artifact ) + " not updated on repository \""
179 + targetRepository.getRepository().getId() + "\"." );
181 catch ( ProxyException e )
183 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
184 targetRepository, localFile, e, previousExceptions );
188 if ( !previousExceptions.isEmpty() )
190 throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories",
191 previousExceptions );
194 log.debug( "Exhausted all target repositories, artifact " + Keys.toKey( artifact ) + " not found." );
198 FileUtils.deleteQuietly(workingDirectory);
204 public File fetchFromProxies( ManagedRepositoryContent repository, String path )
206 File workingDir = createWorkingDirectory(repository);
209 File localFile = new File( repository.getRepoRoot(), path );
211 // no update policies for these paths
212 if ( localFile.exists() )
217 Properties requestProperties = new Properties();
218 requestProperties.setProperty( "filetype", "resource" );
219 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
221 List<ProxyConnector> connectors = getProxyConnectors( repository );
222 for ( ProxyConnector connector : connectors )
224 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
225 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
227 String targetPath = path;
231 File downloadedFile =
232 transferFile( connector, targetRepository, targetPath, repository, workingDir, localFile, requestProperties, false );
234 if ( fileExists( downloadedFile ) )
236 log.debug( "Successfully transferred: " + downloadedFile.getAbsolutePath() );
237 return downloadedFile;
240 catch ( NotFoundException e )
242 log.debug( "Resource " + path + " not found on repository \""
243 + targetRepository.getRepository().getId() + "\"." );
245 catch ( NotModifiedException e )
247 log.debug( "Resource " + path + " not updated on repository \""
248 + targetRepository.getRepository().getId() + "\"." );
250 catch ( ProxyException e )
252 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId()
253 + "\" for resource " + path + ", continuing to next repository. Error message: " + e.getMessage() );
254 log.debug( "Full stack trace", e );
258 log.debug( "Exhausted all target repositories, resource " + path + " not found." );
262 FileUtils.deleteQuietly(workingDir);
268 public File fetchMetatadaFromProxies(ManagedRepositoryContent repository, String logicalPath)
270 File workingDir = createWorkingDirectory(repository);
273 File localFile = new File(repository.getRepoRoot(), logicalPath);
275 Properties requestProperties = new Properties();
276 requestProperties.setProperty( "filetype", "metadata" );
277 boolean metadataNeedsUpdating = false;
278 long originalTimestamp = getLastModified( localFile );
280 List<ProxyConnector> connectors = getProxyConnectors( repository );
281 for ( ProxyConnector connector : connectors )
283 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
285 File localRepoFile = toLocalRepoFile( repository, targetRepository, logicalPath );
286 long originalMetadataTimestamp = getLastModified( localRepoFile );
290 transferFile( connector, targetRepository, logicalPath, repository, workingDir, localRepoFile, requestProperties, true );
292 if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
294 metadataNeedsUpdating = true;
297 catch ( NotFoundException e )
299 log.debug( "Metadata " + logicalPath
300 + " not found on remote repository \""
301 + targetRepository.getRepository().getId() + "\".", e );
303 catch ( NotModifiedException e )
305 log.debug( "Metadata " + logicalPath
306 + " not updated on remote repository \""
307 + targetRepository.getRepository().getId() + "\".", e );
309 catch ( ProxyException e )
311 log.warn( "Transfer error from repository \"" + targetRepository.getRepository().getId() +
312 "\" for versioned Metadata " + logicalPath +
313 ", continuing to next repository. Error message: " + e.getMessage() );
314 log.debug( "Full stack trace", e );
318 if ( hasBeenUpdated( localFile, originalTimestamp ) )
320 metadataNeedsUpdating = true;
323 if ( metadataNeedsUpdating || !localFile.exists())
327 metadataTools.updateMetadata( repository, logicalPath );
329 catch ( RepositoryMetadataException e )
331 log.warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
335 if ( fileExists( localFile ) )
342 FileUtils.deleteQuietly(workingDir);
348 private long getLastModified( File file )
350 if ( !file.exists() || !file.isFile() )
355 return file.lastModified();
358 private boolean hasBeenUpdated( File file, long originalLastModified )
360 if ( !file.exists() || !file.isFile() )
365 long currentLastModified = getLastModified( file );
366 return ( currentLastModified > originalLastModified );
369 private File toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository,
372 String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath );
373 return new File( repository.getRepoRoot(), repoPath );
377 * Test if the provided ManagedRepositoryContent has any proxies configured for it.
379 public boolean hasProxies( ManagedRepositoryContent repository )
381 synchronized ( this.proxyConnectorMap )
383 return this.proxyConnectorMap.containsKey( repository.getId() );
387 private File toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact )
389 return repository.toFile( artifact );
393 * Simple method to test if the file exists on the local disk.
395 * @param file the file to test. (may be null)
396 * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File.
398 private boolean fileExists( File file )
405 if ( !file.exists() )
410 if ( !file.isFile() )
419 * Perform the transfer of the file.
421 * @param connector the connector configuration to use.
422 * @param remoteRepository the remote repository get the resource from.
423 * @param remotePath the path in the remote repository to the resource to get.
424 * @param repository the managed repository that will hold the file
425 * @param resource the local file to place the downloaded resource into
426 * @param requestProperties the request properties to utilize for policy handling.
427 * @param executeConsumers whether to execute the consumers after proxying
428 * @return the local file that was downloaded, or null if not downloaded.
429 * @throws NotFoundException if the file was not found on the remote repository.
430 * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository,
431 * but the remote resource is not newer than the local File.
432 * @throws ProxyException if transfer was unsuccessful.
434 private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath,
435 ManagedRepositoryContent repository, File workingDirectory, File resource, Properties requestProperties,
436 boolean executeConsumers )
437 throws ProxyException, NotModifiedException
439 String url = remoteRepository.getURL().getUrl();
440 if ( !url.endsWith( "/" ) )
444 url = url + remotePath;
445 requestProperties.setProperty( "url", url );
447 // Is a whitelist defined?
448 if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
450 // Path must belong to whitelist.
451 if ( !matchesPattern( remotePath, connector.getWhitelist() ) )
453 log.debug( "Path [" + remotePath +
454 "] is not part of defined whitelist (skipping transfer from repository [" +
455 remoteRepository.getRepository().getName() + "])." );
460 // Is target path part of blacklist?
461 if ( matchesPattern( remotePath, connector.getBlacklist() ) )
463 log.debug( "Path [" + remotePath + "] is part of blacklist (skipping transfer from repository [" +
464 remoteRepository.getRepository().getName() + "])." );
468 // Handle pre-download policy
471 validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, resource );
473 catch ( PolicyViolationException e )
475 String emsg = "Transfer not attempted on " + url + " : " + e.getMessage();
476 if ( fileExists( resource ) )
478 log.info( emsg + ": using already present local file." );
486 // MRM-631 - the lightweight wagon does not reset these - remove if we switch to httpclient based wagon
487 String previousHttpProxyHost = System.getProperty( "http.proxyHost" );
488 String previousHttpProxyPort = System.getProperty( "http.proxyPort" );
489 String previousProxyExclusions = System.getProperty( "http.nonProxyHosts" );
493 File tmpResource = null;
498 RepositoryURL repoUrl = remoteRepository.getURL();
499 String protocol = repoUrl.getProtocol();
500 wagon = (Wagon) wagonFactory.getWagon( "wagon#" + protocol );
503 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
506 boolean connected = connectToRepository( connector, wagon, remoteRepository );
509 tmpResource = transferSimpleFile( wagon, remoteRepository, remotePath, repository, workingDirectory, resource );
511 // TODO: these should be used to validate the download based on the policies, not always downloaded to
512 // save on connections since md5 is rarely used
513 tmpSha1 = transferChecksum( wagon, remoteRepository, remotePath, repository, workingDirectory, resource, ".sha1" );
514 tmpMd5 = transferChecksum( wagon, remoteRepository, remotePath, repository, workingDirectory, resource, ".md5" );
517 catch ( NotFoundException e )
519 urlFailureCache.cacheFailure( url );
522 catch ( NotModifiedException e )
524 // Do not cache url here.
527 catch ( ProxyException e )
529 urlFailureCache.cacheFailure( url );
540 // MRM-631 - the lightweight wagon does not reset these - remove if we switch to httpclient based wagon
541 if ( previousHttpProxyHost != null )
543 System.setProperty( "http.proxyHost", previousHttpProxyHost );
547 System.getProperties().remove( "http.proxyHost" );
549 if ( previousHttpProxyPort != null )
551 System.setProperty( "http.proxyPort", previousHttpProxyPort );
555 System.getProperties().remove( "http.proxyPort" );
557 if ( previousProxyExclusions != null )
559 System.setProperty( "http.nonProxyHosts", previousProxyExclusions );
563 System.getProperties().remove( "http.nonProxyHosts" );
566 catch ( ConnectionException e )
568 log.warn( "Unable to disconnect wagon.", e );
573 // Handle post-download policies.
576 validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, tmpResource );
578 catch ( PolicyViolationException e )
580 log.info( "Transfer invalidated from " + url + " : " + e.getMessage() );
581 executeConsumers = false;
582 if ( !fileExists( tmpResource ) )
588 if (resource != null)
590 synchronized (resource.getAbsolutePath().intern())
592 File directory = resource.getParentFile();
593 moveFileIfExists(tmpMd5, directory);
594 moveFileIfExists(tmpSha1, directory);
595 moveFileIfExists(tmpResource, directory);
599 if ( executeConsumers )
601 // Just-in-time update of the index and database by executing the consumers for this artifact
602 consumers.executeConsumers( connector.getSourceRepository().getRepository(), resource );
611 * Moves the file into repository location if it exists
613 * @param fileToMove this could be either the main artifact, sha1 or md5 checksum file.
614 * @param directory directory to write files to
616 private void moveFileIfExists(File fileToMove, File directory) throws ProxyException
618 if (fileToMove != null && fileToMove.exists())
620 File newLocation = new File(directory, fileToMove.getName());
621 moveTempToTarget(fileToMove, newLocation);
627 * Quietly transfer the checksum file from the remote repository to the local file.
630 * @param wagon the wagon instance (should already be connected) to use.
631 * @param remoteRepository the remote repository to transfer from.
632 * @param remotePath the remote path to the resource to get.
633 * @param repository the managed repository that will hold the file
634 * @param localFile the local file that should contain the downloaded contents
635 * @param type the type of checksum to transfer (example: ".md5" or ".sha1")
636 * @throws ProxyException if copying the downloaded file into place did not succeed.
638 private File transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
639 ManagedRepositoryContent repository, File workingDirectory, File localFile, String type )
640 throws ProxyException
642 File hashFile = new File( localFile.getAbsolutePath() + type );
643 File tmpChecksum = new File(workingDirectory, hashFile.getName());
644 String url = remoteRepository.getURL().getUrl() + remotePath;
646 // Transfer checksum does not use the policy.
647 if ( urlFailureCache.hasFailedBefore( url + type ) )
654 transferSimpleFile( wagon, remoteRepository, remotePath + type, repository, workingDirectory, hashFile );
655 log.debug( "Checksum" + type + " Downloaded: " + hashFile );
657 catch ( NotFoundException e )
659 urlFailureCache.cacheFailure( url + type );
660 log.debug( "Transfer failed, checksum not found: " + url );
661 // Consume it, do not pass this on.
663 catch ( NotModifiedException e )
665 log.debug( "Transfer skipped, checksum not modified: " + url );
666 // Consume it, do not pass this on.
668 catch ( ProxyException e )
670 urlFailureCache.cacheFailure( url + type );
671 log.warn( "Transfer failed on checksum: " + url + " : " + e.getMessage(), e );
672 // Critical issue, pass it on.
679 * Perform the transfer of the remote file to the local file specified.
681 * @param wagon the wagon instance to use.
682 * @param remoteRepository the remote repository to use
683 * @param remotePath the remote path to attempt to get
684 * @param repository the managed repository that will hold the file
685 * @param localFile the local file to save to
686 * @return The local file that was transfered.
687 * @throws ProxyException if there was a problem moving the downloaded file into place.
688 * @throws WagonException if there was a problem tranfering the file.
690 private File transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
691 ManagedRepositoryContent repository, File workingDirectory, File localFile )
692 throws ProxyException
694 assert ( remotePath != null );
696 // Transfer the file.
701 temp = new File(workingDirectory, localFile.getName());
703 boolean success = false;
705 if ( !localFile.exists() )
707 log.debug( "Retrieving " + remotePath + " from " + remoteRepository.getRepository().getName() );
708 wagon.get( remotePath, temp );
711 // You wouldn't get here on failure, a WagonException would have been thrown.
712 log.debug( "Downloaded successfully." );
716 log.debug( "Retrieving " + remotePath + " from " + remoteRepository.getRepository().getName()
718 success = wagon.getIfNewer( remotePath, temp, localFile.lastModified() );
721 throw new NotModifiedException(
722 "Not downloaded, as local file is newer than remote side: " + localFile.getAbsolutePath() );
727 log.debug( "Downloaded successfully." );
733 catch ( ResourceDoesNotExistException e )
735 throw new NotFoundException(
736 "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(),
739 catch ( WagonException e )
741 // TODO: shouldn't have to drill into the cause, but TransferFailedException is often not descriptive enough
744 "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage();
745 if ( e.getCause() != null )
747 msg += " (cause: " + e.getCause() + ")";
749 throw new ProxyException( msg, e );
754 * Apply the policies.
756 * @param policies the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
757 * @param settings the map of settings for the policies to execute. (Map of String policy keys, to String policy setting)
758 * @param request the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String,Properties,File)})
759 * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String,Properties,File)})
761 private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings,
762 Properties request, File localFile )
763 throws PolicyViolationException
765 for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
767 String key = entry.getKey();
768 DownloadPolicy policy = entry.getValue();
769 String defaultSetting = policy.getDefaultOption();
770 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
772 log.debug( "Applying [" + key + "] policy with [" + setting + "]" );
775 policy.applyPolicy( setting, request, localFile );
777 catch ( PolicyConfigurationException e )
779 log.error( e.getMessage(), e );
784 private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings,
785 Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
786 File localFile, ProxyException exception, Map<String, Exception> previousExceptions )
787 throws ProxyDownloadException
789 boolean process = true;
790 for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
792 String key = entry.getKey();
793 DownloadErrorPolicy policy = entry.getValue();
794 String defaultSetting = policy.getDefaultOption();
795 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
797 log.debug( "Applying [" + key + "] policy with [" + setting + "]" );
800 // all policies must approve the exception, any can cancel
801 process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions );
807 catch ( PolicyConfigurationException e )
809 log.error( e.getMessage(), e );
815 // if the exception was queued, don't throw it
816 if ( !previousExceptions.containsKey( content.getId() ) )
818 throw new ProxyDownloadException(
819 "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
820 content.getId(), exception );
825 // if the exception was queued, but cancelled, remove it
826 previousExceptions.remove( content.getId() );
829 log.warn( "Transfer error from repository \"" + content.getRepository().getId() + "\" for artifact " +
830 Keys.toKey( artifact ) + ", continuing to next repository. Error message: " + exception.getMessage() );
831 log.debug( "Full stack trace", exception );
835 * Creates a working directory in the repository root for this request
837 * @return file location of working directory
839 private File createWorkingDirectory(ManagedRepositoryContent repository)
841 //TODO: This is ugly - lets actually clean this up when we get the new repository api
844 File tmpDir = File.createTempFile(".workingdirectory", null, new File(repository.getRepoRoot()));
849 catch (IOException e)
851 throw new RuntimeException("Could not create working directory for this request", e);
856 * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles
857 * its downloaded files.
859 * @param temp The completed download file
860 * @param target The final location of the downloaded file
861 * @throws ProxyException when the temp file cannot replace the target file
863 private void moveTempToTarget( File temp, File target )
864 throws ProxyException
866 if ( target.exists() && !target.delete() )
868 throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() );
871 target.getParentFile().mkdirs();
872 if ( !temp.renameTo( target ) )
874 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
878 FileUtils.copyFile( temp, target );
880 catch ( IOException e )
884 log.debug("Tried to copy file " + temp.getName() + " to " + target.getAbsolutePath() + " but file with this name already exists.");
888 throw new ProxyException( "Cannot copy tmp file " + temp.getAbsolutePath() + " to its final location", e );
893 FileUtils.deleteQuietly(temp);
899 * Using wagon, connect to the remote repository.
901 * @param connector the connector configuration to utilize (for obtaining network proxy configuration from)
902 * @param wagon the wagon instance to establish the connection on.
903 * @param remoteRepository the remote repository to connect to.
904 * @return true if the connection was successful. false if not connected.
906 private boolean connectToRepository( ProxyConnector connector, Wagon wagon,
907 RemoteRepositoryContent remoteRepository )
909 boolean connected = false;
911 final ProxyInfo networkProxy;
912 synchronized ( this.networkProxyMap )
914 networkProxy = (ProxyInfo) this.networkProxyMap.get( connector.getProxyId() );
917 if ( log.isDebugEnabled() )
919 if ( networkProxy != null )
921 // TODO: move to proxyInfo.toString()
923 "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
924 + " to connect to remote repository " + remoteRepository.getURL();
925 if ( networkProxy.getNonProxyHosts() != null )
927 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
929 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
931 msg += "; as user: " + networkProxy.getUserName();
937 AuthenticationInfo authInfo = null;
938 String username = remoteRepository.getRepository().getUsername();
939 String password = remoteRepository.getRepository().getPassword();
941 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
943 log.debug( "Using username " + username + " to connect to remote repository "
944 + remoteRepository.getURL() );
945 authInfo = new AuthenticationInfo();
946 authInfo.setUserName( username );
947 authInfo.setPassword( password );
950 //Convert seconds to milliseconds
951 int timeoutInMilliseconds = remoteRepository.getRepository().getTimeout() * 1000;
954 wagon.setTimeout(timeoutInMilliseconds);
958 Repository wagonRepository = new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
959 wagon.connect( wagonRepository, authInfo, networkProxy );
962 catch ( ConnectionException e )
965 "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
968 catch ( AuthenticationException e )
971 "Could not connect to " + remoteRepository.getRepository().getName() + ": " + e.getMessage() );
979 * Tests whitelist and blacklist patterns against path.
981 * @param path the path to test.
982 * @param patterns the list of patterns to check.
983 * @return true if the path matches at least 1 pattern in the provided patterns list.
985 private boolean matchesPattern( String path, List<String> patterns )
987 if ( CollectionUtils.isEmpty( patterns ) )
992 for ( String pattern : patterns )
994 if ( SelectorUtils.matchPath( pattern, path, false ) )
1004 * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
1006 public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository )
1008 synchronized ( this.proxyConnectorMap )
1010 List<ProxyConnector> ret = (List<ProxyConnector>) this.proxyConnectorMap.get( repository.getId() );
1013 return Collections.emptyList();
1016 Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
1021 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1023 if ( ConfigurationNames.isNetworkProxy( propertyName ) ||
1024 ConfigurationNames.isManagedRepositories( propertyName ) ||
1025 ConfigurationNames.isRemoteRepositories( propertyName ) ||
1026 ConfigurationNames.isProxyConnector( propertyName ) )
1028 initConnectorsAndNetworkProxies();
1032 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1037 @SuppressWarnings("unchecked")
1038 private void initConnectorsAndNetworkProxies()
1040 synchronized ( this.proxyConnectorMap )
1042 ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator();
1043 this.proxyConnectorMap.clear();
1045 List<ProxyConnectorConfiguration> proxyConfigs = archivaConfiguration.getConfiguration()
1046 .getProxyConnectors();
1047 for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs )
1049 String key = proxyConfig.getSourceRepoId();
1053 // Create connector object.
1054 ProxyConnector connector = new ProxyConnector();
1056 connector.setSourceRepository( repositoryFactory.getManagedRepositoryContent( proxyConfig
1057 .getSourceRepoId() ) );
1058 connector.setTargetRepository( repositoryFactory.getRemoteRepositoryContent( proxyConfig
1059 .getTargetRepoId() ) );
1061 connector.setProxyId( proxyConfig.getProxyId() );
1062 connector.setPolicies( proxyConfig.getPolicies() );
1063 connector.setOrder( proxyConfig.getOrder() );
1065 // Copy any blacklist patterns.
1066 List<String> blacklist = new ArrayList<String>();
1067 if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) )
1069 blacklist.addAll( proxyConfig.getBlackListPatterns() );
1071 connector.setBlacklist( blacklist );
1073 // Copy any whitelist patterns.
1074 List<String> whitelist = new ArrayList<String>();
1075 if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) )
1077 whitelist.addAll( proxyConfig.getWhiteListPatterns() );
1079 connector.setWhitelist( whitelist );
1081 // Get other connectors
1082 List<ProxyConnector> connectors = this.proxyConnectorMap.get( key );
1083 if ( connectors == null )
1085 // Create if we are the first.
1086 connectors = new ArrayList<ProxyConnector>();
1089 // Add the connector.
1090 connectors.add( connector );
1092 // Ensure the list is sorted.
1093 Collections.sort( connectors, proxyOrderSorter );
1095 // Set the key to the list of connectors.
1096 this.proxyConnectorMap.put( key, connectors );
1098 catch ( RepositoryNotFoundException e )
1100 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
1102 catch ( RepositoryException e )
1104 log.warn( "Unable to use proxy connector: " + e.getMessage(), e );
1110 synchronized ( this.networkProxyMap )
1112 this.networkProxyMap.clear();
1114 List<NetworkProxyConfiguration> networkProxies = archivaConfiguration.getConfiguration().getNetworkProxies();
1115 for ( NetworkProxyConfiguration networkProxyConfig : networkProxies )
1117 String key = networkProxyConfig.getId();
1119 ProxyInfo proxy = new ProxyInfo();
1121 proxy.setType( networkProxyConfig.getProtocol() );
1122 proxy.setHost( networkProxyConfig.getHost() );
1123 proxy.setPort( networkProxyConfig.getPort() );
1124 proxy.setUserName( networkProxyConfig.getUsername() );
1125 proxy.setPassword( networkProxyConfig.getPassword() );
1127 this.networkProxyMap.put( key, proxy );
1132 public void initialize()
1133 throws InitializationException
1135 initConnectorsAndNetworkProxies();
1136 archivaConfiguration.addChangeListener( this );