]> source.dussan.org Git - archiva.git/blob
4872427b28a27979e6099e7984f9ba33c138f50a
[archiva.git] /
1 package org.apache.maven.archiva.proxy;
2
3 /*
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
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
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
19  * under the License.
20  */
21
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.ProjectReference;
31 import org.apache.maven.archiva.model.RepositoryURL;
32 import org.apache.maven.archiva.model.VersionedReference;
33 import org.apache.maven.archiva.policies.DownloadPolicy;
34 import org.apache.maven.archiva.policies.PostDownloadPolicy;
35 import org.apache.maven.archiva.policies.PreDownloadPolicy;
36 import org.apache.maven.archiva.policies.urlcache.UrlFailureCache;
37 import org.apache.maven.archiva.repository.ContentNotFoundException;
38 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
39 import org.apache.maven.archiva.repository.RemoteRepositoryContent;
40 import org.apache.maven.archiva.repository.RepositoryContentFactory;
41 import org.apache.maven.archiva.repository.RepositoryException;
42 import org.apache.maven.archiva.repository.RepositoryNotFoundException;
43 import org.apache.maven.archiva.repository.layout.LayoutException;
44 import org.apache.maven.archiva.repository.metadata.MetadataTools;
45 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
46 import org.apache.maven.archiva.repository.scanner.RepositoryContentConsumers;
47 import org.apache.maven.wagon.ConnectionException;
48 import org.apache.maven.wagon.ResourceDoesNotExistException;
49 import org.apache.maven.wagon.Wagon;
50 import org.apache.maven.wagon.WagonException;
51 import org.apache.maven.wagon.authentication.AuthenticationException;
52 import org.apache.maven.wagon.authentication.AuthenticationInfo;
53 import org.apache.maven.wagon.proxy.ProxyInfo;
54 import org.apache.maven.wagon.repository.Repository;
55 import org.codehaus.plexus.logging.AbstractLogEnabled;
56 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable;
57 import org.codehaus.plexus.personality.plexus.lifecycle.phase.InitializationException;
58 import org.codehaus.plexus.registry.Registry;
59 import org.codehaus.plexus.registry.RegistryListener;
60 import org.codehaus.plexus.util.SelectorUtils;
61
62 import java.io.File;
63 import java.io.IOException;
64 import java.util.ArrayList;
65 import java.util.Collections;
66 import java.util.HashMap;
67 import java.util.List;
68 import java.util.Map;
69 import java.util.Properties;
70 import java.util.Map.Entry;
71
72 /**
73  * DefaultRepositoryProxyConnectors
74  *
75  * @author <a href="mailto:joakime@apache.org">Joakim Erdfelt</a>
76  * @version $Id$
77  * @plexus.component role-hint="default"
78  */
79 public class DefaultRepositoryProxyConnectors
80     extends AbstractLogEnabled
81     implements RepositoryProxyConnectors, RegistryListener, Initializable
82 {
83     /**
84      * @plexus.requirement
85      */
86     private ArchivaConfiguration archivaConfiguration;
87
88     /**
89      * @plexus.requirement role="org.apache.maven.wagon.Wagon"
90      */
91     private Map<String, Wagon> wagons;
92
93     /**
94      * @plexus.requirement
95      */
96     private RepositoryContentFactory repositoryFactory;
97
98     /**
99      * @plexus.requirement
100      */
101     private MetadataTools metadataTools;
102
103     /**
104      * @plexus.requirement role="org.apache.maven.archiva.policies.PreDownloadPolicy"
105      */
106     private Map<String, PreDownloadPolicy> preDownloadPolicies;
107
108     /**
109      * @plexus.requirement role="org.apache.maven.archiva.policies.PostDownloadPolicy"
110      */
111     private Map<String, PostDownloadPolicy> postDownloadPolicies;
112
113     /**
114      * @plexus.requirement role-hint="default"
115      */
116     private UrlFailureCache urlFailureCache;
117
118     private Map<String, List<ProxyConnector>> proxyConnectorMap = new HashMap<String, List<ProxyConnector>>();
119
120     private Map<String, ProxyInfo> networkProxyMap = new HashMap<String, ProxyInfo>();
121
122     /**
123      * @plexus.requirement
124      */
125     private RepositoryContentConsumers consumers;
126
127     /**
128      * Fetch an artifact from a remote repository.
129      *
130      * @param repository the managed repository to utilize for the request.
131      * @param artifact   the artifact reference to fetch.
132      * @return the local file in the managed repository that was fetched, or null if the artifact was not (or
133      *         could not be) fetched.
134      * @throws ProxyException if there was a problem fetching the artifact.
135      */
136     public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
137         throws ProxyException
138     {
139         File localFile = toLocalFile( repository, artifact );
140
141         Properties requestProperties = new Properties();
142         requestProperties.setProperty( "version", artifact.getVersion() );
143
144         List<ProxyConnector> connectors = getProxyConnectors( repository );
145         for ( ProxyConnector connector : connectors )
146         {
147             RemoteRepositoryContent targetRepository = connector.getTargetRepository();
148             String targetPath = targetRepository.toPath( artifact );
149
150             File downloadedFile = transferFile( connector, targetRepository, targetPath, localFile, requestProperties );
151
152             if ( fileExists( downloadedFile ) )
153             {
154                 getLogger().debug( "Successfully transfered: " + downloadedFile.getAbsolutePath() );
155                 return downloadedFile;
156             }
157         }
158
159         return null;
160     }
161
162     /**
163      * Fetch, from the proxies, a metadata.xml file for the groupId:artifactId:version metadata contents.
164      *
165      * @return the (local) metadata file that was fetched/merged/updated, or null if no metadata file exists.
166      */
167     public File fetchFromProxies( ManagedRepositoryContent repository, VersionedReference metadata )
168         throws ProxyException
169     {
170         File localFile = toLocalFile( repository, metadata );
171
172         Properties requestProperties = new Properties();
173         boolean metadataNeedsUpdating = false;
174         long originalTimestamp = getLastModified( localFile );
175
176         List<ProxyConnector> connectors = getProxyConnectors( repository );
177         for ( ProxyConnector connector : connectors )
178         {
179             RemoteRepositoryContent targetRepository = connector.getTargetRepository();
180             String targetPath = metadataTools.toPath( metadata );
181
182             File localRepoFile = toLocalRepoFile( repository, targetRepository, targetPath );
183             long originalMetadataTimestamp = getLastModified( localRepoFile );
184             transferFile( connector, targetRepository, targetPath, localRepoFile, requestProperties );
185
186             if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
187             {
188                 metadataNeedsUpdating = true;
189             }
190         }
191
192         if ( hasBeenUpdated( localFile, originalTimestamp ) )
193         {
194             metadataNeedsUpdating = true;
195         }
196
197         if ( metadataNeedsUpdating )
198         {
199             try
200             {
201                 metadataTools.updateMetadata( repository, metadata );
202             }
203             catch ( LayoutException e )
204             {
205                 getLogger().warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage() );
206                 // TODO: add into repository report?
207             }
208             catch ( RepositoryMetadataException e )
209             {
210                 getLogger()
211                     .warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
212                 // TODO: add into repository report?
213             }
214             catch ( IOException e )
215             {
216                 getLogger()
217                     .warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
218                 // TODO: add into repository report?
219             }
220             catch ( ContentNotFoundException e )
221             {
222                 getLogger()
223                     .warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
224                 // TODO: add into repository report?
225             }
226         }
227
228         if ( fileExists( localFile ) )
229         {
230             return localFile;
231         }
232
233         return null;
234     }
235
236     private long getLastModified( File file )
237     {
238         if ( !file.exists() || !file.isFile() )
239         {
240             return 0;
241         }
242
243         return file.lastModified();
244     }
245     
246     private boolean hasBeenUpdated( File file, long originalLastModified )
247     {
248         if ( !file.exists() || !file.isFile() )
249         {
250             return false;
251         }
252         
253         long currentLastModified = getLastModified( file );
254         return ( currentLastModified > originalLastModified );
255     }
256
257     /**
258      * Fetch from the proxies a metadata.xml file for the groupId:artifactId metadata contents.
259      *
260      * @return the (local) metadata file that was fetched/merged/updated, or null if no metadata file exists.
261      */
262     public File fetchFromProxies( ManagedRepositoryContent repository, ProjectReference metadata )
263         throws ProxyException
264     {
265         File localFile = toLocalFile( repository, metadata );
266
267         Properties requestProperties = new Properties();
268         boolean metadataNeedsUpdating = false;
269         long originalTimestamp = getLastModified( localFile );
270
271         List<ProxyConnector> connectors = getProxyConnectors( repository );
272         for ( ProxyConnector connector : connectors )
273         {
274             RemoteRepositoryContent targetRepository = connector.getTargetRepository();
275             String targetPath = metadataTools.toPath( metadata );
276
277             File localRepoFile = toLocalRepoFile( repository, targetRepository, targetPath );
278             long originalMetadataTimestamp = getLastModified( localRepoFile );
279             transferFile( connector, targetRepository, targetPath, localRepoFile, requestProperties );
280
281             if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
282             {
283                 metadataNeedsUpdating = true;
284             }
285         }
286
287         if ( hasBeenUpdated( localFile, originalTimestamp ) )
288         {
289             metadataNeedsUpdating = true;
290         }
291
292         if ( metadataNeedsUpdating )
293         {
294             try
295             {
296                 metadataTools.updateMetadata( repository, metadata );
297             }
298             catch ( LayoutException e )
299             {
300                 getLogger().warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage() );
301                 // TODO: add into repository report?
302             }
303             catch ( RepositoryMetadataException e )
304             {
305                 getLogger()
306                     .warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
307                 // TODO: add into repository report?
308             }
309             catch ( IOException e )
310             {
311                 getLogger()
312                     .warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
313                 // TODO: add into repository report?
314             }
315             catch ( ContentNotFoundException e )
316             {
317                 getLogger()
318                     .warn( "Unable to update metadata " + localFile.getAbsolutePath() + ": " + e.getMessage(), e );
319                 // TODO: add into repository report?
320             }
321         }
322
323         if ( fileExists( localFile ) )
324         {
325             return localFile;
326         }
327
328         return null;
329     }
330
331     private File toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository,
332                                   String targetPath )
333     {
334         String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath );
335         return new File( repository.getRepoRoot(), repoPath );
336     }
337
338     /**
339      * Test if the provided ManagedRepositoryContent has any proxies configured for it.
340      */
341     public boolean hasProxies( ManagedRepositoryContent repository )
342     {
343         synchronized ( this.proxyConnectorMap )
344         {
345             return this.proxyConnectorMap.containsKey( repository.getId() );
346         }
347     }
348
349     private File toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact )
350         throws ProxyException
351     {
352         return repository.toFile( artifact );
353     }
354
355     private File toLocalFile( ManagedRepositoryContent repository, ProjectReference metadata )
356         throws ProxyException
357     {
358         String sourcePath = metadataTools.toPath( metadata );
359         return new File( repository.getRepoRoot(), sourcePath );
360     }
361
362     private File toLocalFile( ManagedRepositoryContent repository, VersionedReference metadata )
363         throws ProxyException
364     {
365         String sourcePath = metadataTools.toPath( metadata );
366         return new File( repository.getRepoRoot(), sourcePath );
367     }
368
369     /**
370      * Simple method to test if the file exists on the local disk.
371      *
372      * @param file the file to test. (may be null)
373      * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File.
374      */
375     private boolean fileExists( File file )
376     {
377         if ( file == null )
378         {
379             return false;
380         }
381
382         if ( !file.exists() )
383         {
384             return false;
385         }
386
387         if ( !file.isFile() )
388         {
389             return false;
390         }
391
392         return true;
393     }
394
395     /**
396      * Perform the transfer of the file.
397      *
398      * @param connector         the connector configuration to use.
399      * @param remoteRepository  the remote repository get the resource from.
400      * @param remotePath        the path in the remote repository to the resource to get.
401      * @param localFile         the local file to place the downloaded resource into
402      * @param requestProperties the request properties to utilize for policy handling.
403      * @return the local file that was downloaded, or null if not downloaded.
404      * @throws ProxyException if transfer was unsuccessful.
405      */
406     private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath,
407                                File localFile, Properties requestProperties )
408         throws ProxyException
409     {
410         String url = remoteRepository.getURL().getUrl() + remotePath;
411         requestProperties.setProperty( "url", url );
412
413         // Is a whitelist defined?
414         if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
415         {
416             // Path must belong to whitelist.
417             if ( !matchesPattern( remotePath, connector.getWhitelist() ) )
418             {
419                 getLogger().debug( "Path [" + remotePath + "] is not part of defined whitelist (skipping transfer)." );
420                 return null;
421             }
422         }
423
424         // Is target path part of blacklist?
425         if ( matchesPattern( remotePath, connector.getBlacklist() ) )
426         {
427             getLogger().debug( "Path [" + remotePath + "] is part of blacklist (skipping transfer)." );
428             return null;
429         }
430
431         // Handle pre-download policy
432         if ( !applyPolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, localFile ) )
433         {
434             getLogger().debug( "Failed pre-download policies - " + localFile.getAbsolutePath() );
435
436             if ( fileExists( localFile ) )
437             {
438                 return localFile;
439             }
440
441             return null;
442         }
443
444         Wagon wagon = null;
445         try
446         {
447             RepositoryURL repoUrl = remoteRepository.getURL();
448             String protocol = repoUrl.getProtocol();
449             wagon = (Wagon) wagons.get( protocol );
450             if ( wagon == null )
451             {
452                 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
453             }
454
455             boolean connected = connectToRepository( connector, wagon, remoteRepository );
456             if ( connected )
457             {
458                 localFile = transferSimpleFile( wagon, remoteRepository, remotePath, localFile );
459
460                 transferChecksum( wagon, remoteRepository, remotePath, localFile, ".sha1" );
461                 transferChecksum( wagon, remoteRepository, remotePath, localFile, ".md5" );
462             }
463         }
464         catch ( ResourceDoesNotExistException e )
465         {
466             // Do not cache url here.
467             return null;
468         }
469         catch ( WagonException e )
470         {
471             urlFailureCache.cacheFailure( url );
472             return null;
473         }
474         finally
475         {
476             if ( wagon != null )
477             {
478                 try
479                 {
480                     wagon.disconnect();
481                 }
482                 catch ( ConnectionException e )
483                 {
484                     getLogger().warn( "Unable to disconnect wagon.", e );
485                 }
486             }
487         }
488
489         // Handle post-download policies.
490         if ( !applyPolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, localFile ) )
491         {
492             getLogger().debug( "Failed post-download policies - " + localFile.getAbsolutePath() );
493
494             if ( fileExists( localFile ) )
495             {
496                 return localFile;
497             }
498
499             return null;
500         }
501
502         // Just-in-time update of the index and database by executing the consumers for this artifact
503         consumers.executeConsumers( connector.getSourceRepository().getRepository(), localFile );
504
505         // Everything passes.
506         return localFile;
507     }
508
509     /**
510      * Quietly transfer the checksum file from the remote repository to the local file.
511      * <p/>
512      * NOTE: This will not throw a WagonException if the checksum is unable to be downloaded.
513      *
514      * @param wagon            the wagon instance (should already be connected) to use.
515      * @param remoteRepository the remote repository to transfer from.
516      * @param remotePath       the remote path to the resource to get.
517      * @param localFile        the local file that should contain the downloaded contents
518      * @param type             the type of checksum to transfer (example: ".md5" or ".sha1")
519      * @throws ProxyException if copying the downloaded file into place did not succeed.
520      */
521     private void transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
522                                    File localFile, String type )
523         throws ProxyException
524     {
525         String url = remoteRepository.getURL().getUrl() + remotePath;
526
527         // Transfer checksum does not use the policy. 
528         if ( urlFailureCache.hasFailedBefore( url + type ) )
529         {
530             return;
531         }
532
533         try
534         {
535             File hashFile = new File( localFile.getAbsolutePath() + type );
536             transferSimpleFile( wagon, remoteRepository, remotePath + type, hashFile );
537             getLogger().debug( "Checksum" + type + " Downloaded: " + hashFile );
538         }
539         catch ( ResourceDoesNotExistException e )
540         {
541             getLogger().debug( "Checksum" + type + " Not Download: " + e.getMessage() );
542         }
543         catch ( WagonException e )
544         {
545             urlFailureCache.cacheFailure( url + type );
546             getLogger().warn( "Transfer failed on checksum: " + url + " : " + e.getMessage(), e );
547         }
548     }
549
550     /**
551      * Perform the transfer of the remote file to the local file specified.
552      *
553      * @param wagon            the wagon instance to use.
554      * @param remoteRepository the remote repository to use
555      * @param remotePath       the remote path to attempt to get
556      * @param localFile        the local file to save to
557      * @return The local file that was transfered.
558      * @throws ProxyException if there was a problem moving the downloaded file into place.
559      * @throws WagonException if there was a problem tranfering the file.
560      */
561     private File transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
562                                      File localFile )
563         throws ProxyException, WagonException
564     {
565         assert ( remotePath != null );
566
567         // Transfer the file.
568         File temp = null;
569
570         try
571         {
572             temp = new File( localFile.getAbsolutePath() + ".tmp" );
573
574             boolean success = false;
575
576             if ( localFile.exists() )
577             {
578                 getLogger().debug( "Retrieving " + remotePath + " from " + remoteRepository.getRepository().getName() );
579                 wagon.get( remotePath, temp );
580                 success = true;
581
582                 if ( temp.exists() )
583                 {
584                     moveTempToTarget( temp, localFile );
585                 }
586
587                 // You wouldn't get here on failure, a WagonException would have been thrown.
588                 getLogger().debug( "Downloaded successfully." );
589             }
590             else
591             {
592                 getLogger().debug( "Retrieving " + remotePath + " from " + remoteRepository.getRepository().getName()
593                                        + " if updated" );
594                 success = wagon.getIfNewer( remotePath, temp, localFile.lastModified() );
595                 if ( !success )
596                 {
597                     getLogger().debug(
598                                       "Not downloaded, as local file is newer than remote side: "
599                                           + localFile.getAbsolutePath() );
600                 }
601                 else if ( temp.exists() )
602                 {
603                     getLogger().debug( "Downloaded successfully." );
604                     moveTempToTarget( temp, localFile );
605                 }
606             }
607
608             return localFile;
609         }
610         catch ( ResourceDoesNotExistException e )
611         {
612             getLogger().debug( "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: "
613                                    + e.getMessage() );
614             throw e;
615         }
616         catch ( WagonException e )
617         {
618             getLogger().warn( "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:"
619                                   + e.getMessage(), e );
620             throw e;
621         }
622         finally
623         {
624             if ( temp != null )
625             {
626                 temp.delete();
627             }
628         }
629     }
630
631     /**
632      * Apply the policies.
633      *
634      * @param policies  the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
635      * @param settings  the map of settings for the policies to execute. (Map of String policy keys, to String policy setting)
636      * @param request   the request properties (utilized by the {@link DownloadPolicy#applyPolicy(String,Properties,File)})
637      * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(String,Properties,File)})
638      * @return true if all of the policies passed, false if a policy failed.
639      */
640     private boolean applyPolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings,
641                                    Properties request, File localFile )
642     {
643         for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
644         {
645             String key = (String) entry.getKey();
646             DownloadPolicy policy = entry.getValue();
647             String defaultSetting = policy.getDefaultOption();
648             String setting = StringUtils.defaultString( (String) settings.get( key ), defaultSetting );
649
650             getLogger().debug( "Applying [" + key + "] policy with [" + setting + "]" );
651             if ( !policy.applyPolicy( setting, request, localFile ) )
652             {
653                 getLogger().debug( "Didn't pass the [" + key + "] policy." );
654                 return false;
655             }
656         }
657         return true;
658     }
659
660     /**
661      * Used to move the temporary file to its real destination.  This is patterned from the way WagonManager handles
662      * its downloaded files.
663      *
664      * @param temp   The completed download file
665      * @param target The final location of the downloaded file
666      * @throws ProxyException when the temp file cannot replace the target file
667      */
668     private void moveTempToTarget( File temp, File target )
669         throws ProxyException
670     {
671         if ( target.exists() && !target.delete() )
672         {
673             throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() );
674         }
675
676         if ( !temp.renameTo( target ) )
677         {
678             getLogger().warn( "Unable to rename tmp file to its final name... resorting to copy command." );
679
680             try
681             {
682                 FileUtils.copyFile( temp, target );
683             }
684             catch ( IOException e )
685             {
686                 throw new ProxyException( "Cannot copy tmp file to its final location", e );
687             }
688             finally
689             {
690                 temp.delete();
691             }
692         }
693     }
694
695     /**
696      * Using wagon, connect to the remote repository.
697      *
698      * @param connector        the connector configuration to utilize (for obtaining network proxy configuration from)
699      * @param wagon            the wagon instance to establish the connection on.
700      * @param remoteRepository the remote repository to connect to.
701      * @return true if the connection was successful. false if not connected.
702      */
703     private boolean connectToRepository( ProxyConnector connector, Wagon wagon, RemoteRepositoryContent remoteRepository )
704     {
705         boolean connected = false;
706
707         ProxyInfo networkProxy = null;
708         synchronized ( this.networkProxyMap )
709         {
710             networkProxy = (ProxyInfo) this.networkProxyMap.get( connector.getProxyId() );
711         }
712
713         try
714         {
715             AuthenticationInfo authInfo = null;
716             String username = remoteRepository.getRepository().getUsername();
717             String password = remoteRepository.getRepository().getPassword();
718             if ( username != null && password != null )
719             {
720                 getLogger().debug(
721                                    "Using username " + username + " to connect to remote repository "
722                                        + remoteRepository.getURL() );
723                 authInfo = new AuthenticationInfo();
724                 authInfo.setUserName( username );
725                 authInfo.setPassword( password );
726             }
727             else
728             {
729                 getLogger().debug( "No authentication for remote repository needed" );
730             }
731
732             Repository wagonRepository = new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
733             if ( networkProxy != null )
734             {
735                 wagon.connect( wagonRepository, authInfo, networkProxy );
736             }
737             else
738             {
739                 wagon.connect( wagonRepository, authInfo );
740             }
741             connected = true;
742         }
743         catch ( ConnectionException e )
744         {
745             getLogger().warn(
746                               "Could not connect to " + remoteRepository.getRepository().getName() + ": "
747                                   + e.getMessage() );
748             connected = false;
749         }
750         catch ( AuthenticationException e )
751         {
752             getLogger().warn(
753                               "Could not connect to " + remoteRepository.getRepository().getName() + ": "
754                                   + e.getMessage() );
755             connected = false;
756         }
757
758         return connected;
759     }
760
761     /**
762      * Tests whitelist and blacklist patterns against path.
763      *
764      * @param path     the path to test.
765      * @param patterns the list of patterns to check.
766      * @return true if the path matches at least 1 pattern in the provided patterns list.
767      */
768     private boolean matchesPattern( String path, List<String> patterns )
769     {
770         if ( CollectionUtils.isEmpty( patterns ) )
771         {
772             return false;
773         }
774
775         for ( String pattern : patterns )
776         {
777             if ( SelectorUtils.matchPath( pattern, path, false ) )
778             {
779                 return true;
780             }
781         }
782
783         return false;
784     }
785
786     /**
787      * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
788      */
789     public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository )
790     {
791         synchronized ( this.proxyConnectorMap )
792         {
793             List<ProxyConnector> ret = (List<ProxyConnector>) this.proxyConnectorMap.get( repository.getId() );
794             if ( ret == null )
795             {
796                 return Collections.EMPTY_LIST;
797             }
798
799             Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
800             return ret;
801         }
802     }
803
804     public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
805     {
806         if ( ConfigurationNames.isNetworkProxy( propertyName )
807             || ConfigurationNames.isManagedRepositories( propertyName )
808             || ConfigurationNames.isRemoteRepositories( propertyName )
809             || ConfigurationNames.isProxyConnector( propertyName ) )
810         {
811             initConnectorsAndNetworkProxies();
812         }
813     }
814
815     public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
816     {
817         /* do nothing */
818     }
819
820     private void initConnectorsAndNetworkProxies()
821     {
822         synchronized ( this.proxyConnectorMap )
823         {
824             ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator();
825             this.proxyConnectorMap.clear();
826
827             List<ProxyConnectorConfiguration> proxyConfigs = archivaConfiguration.getConfiguration()
828                 .getProxyConnectors();
829             for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs )
830             {
831                 String key = proxyConfig.getSourceRepoId();
832
833                 try
834                 {
835                     // Create connector object.
836                     ProxyConnector connector = new ProxyConnector();
837
838                     connector.setSourceRepository( repositoryFactory.getManagedRepositoryContent( proxyConfig
839                         .getSourceRepoId() ) );
840                     connector.setTargetRepository( repositoryFactory.getRemoteRepositoryContent( proxyConfig
841                         .getTargetRepoId() ) );
842
843                     connector.setProxyId( proxyConfig.getProxyId() );
844                     connector.setPolicies( proxyConfig.getPolicies() );
845                     connector.setOrder( proxyConfig.getOrder() );
846
847                     // Copy any blacklist patterns.
848                     List<String> blacklist = new ArrayList<String>();
849                     if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) )
850                     {
851                         blacklist.addAll( proxyConfig.getBlackListPatterns() );
852                     }
853                     connector.setBlacklist( blacklist );
854
855                     // Copy any whitelist patterns.
856                     List<String> whitelist = new ArrayList<String>();
857                     if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) )
858                     {
859                         whitelist.addAll( proxyConfig.getWhiteListPatterns() );
860                     }
861                     connector.setWhitelist( whitelist );
862
863                     // Get other connectors
864                     List<ProxyConnector> connectors = this.proxyConnectorMap.get( key );
865                     if ( connectors == null )
866                     {
867                         // Create if we are the first.
868                         connectors = new ArrayList<ProxyConnector>();
869                     }
870
871                     // Add the connector.
872                     connectors.add( connector );
873
874                     // Ensure the list is sorted.
875                     Collections.sort( connectors, proxyOrderSorter );
876
877                     // Set the key to the list of connectors.
878                     this.proxyConnectorMap.put( key, connectors );
879                 }
880                 catch ( RepositoryNotFoundException e )
881                 {
882                     getLogger().warn( "Unable to use proxy connector: " + e.getMessage(), e );
883                 }
884                 catch ( RepositoryException e )
885                 {
886                     getLogger().warn( "Unable to use proxy connector: " + e.getMessage(), e );
887                 }
888             }
889
890         }
891
892         synchronized ( this.networkProxyMap )
893         {
894             this.networkProxyMap.clear();
895
896             List<NetworkProxyConfiguration> networkProxies = archivaConfiguration.getConfiguration()
897                 .getNetworkProxies();
898             for ( NetworkProxyConfiguration networkProxyConfig : networkProxies )
899             {
900                 String key = networkProxyConfig.getId();
901
902                 ProxyInfo proxy = new ProxyInfo();
903
904                 proxy.setType( networkProxyConfig.getProtocol() );
905                 proxy.setHost( networkProxyConfig.getHost() );
906                 proxy.setPort( networkProxyConfig.getPort() );
907                 proxy.setUserName( networkProxyConfig.getUsername() );
908                 proxy.setPassword( networkProxyConfig.getPassword() );
909
910                 this.networkProxyMap.put( key, proxy );
911             }
912         }
913     }
914
915     public void initialize()
916         throws InitializationException
917     {
918         initConnectorsAndNetworkProxies();
919         archivaConfiguration.addChangeListener( this );
920     }
921 }