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