]> source.dussan.org Git - archiva.git/blob
d0105811679fcd8d997e4295a492338eef1d1569
[archiva.git] /
1 package org.apache.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.archiva.checksum.ChecksumAlgorithm;
23 import org.apache.archiva.checksum.ChecksumUtil;
24 import org.apache.archiva.common.filelock.FileLockManager;
25 import org.apache.archiva.common.utils.PathUtil;
26 import org.apache.archiva.configuration.ArchivaConfiguration;
27 import org.apache.archiva.configuration.ProxyConnectorConfiguration;
28 import org.apache.archiva.configuration.ProxyConnectorRuleConfiguration;
29 import org.apache.archiva.model.ArtifactReference;
30 import org.apache.archiva.model.Keys;
31 import org.apache.archiva.policies.DownloadErrorPolicy;
32 import org.apache.archiva.policies.DownloadPolicy;
33 import org.apache.archiva.policies.Policy;
34 import org.apache.archiva.policies.PolicyConfigurationException;
35 import org.apache.archiva.policies.PolicyOption;
36 import org.apache.archiva.policies.PolicyViolationException;
37 import org.apache.archiva.policies.PostDownloadPolicy;
38 import org.apache.archiva.policies.PreDownloadPolicy;
39 import org.apache.archiva.policies.ProxyDownloadException;
40 import org.apache.archiva.policies.urlcache.UrlFailureCache;
41 import org.apache.archiva.proxy.model.NetworkProxy;
42 import org.apache.archiva.proxy.model.ProxyConnector;
43 import org.apache.archiva.proxy.model.ProxyFetchResult;
44 import org.apache.archiva.proxy.model.RepositoryProxyHandler;
45 import org.apache.archiva.components.taskqueue.TaskQueueException;
46 import org.apache.archiva.repository.BaseRepositoryContentLayout;
47 import org.apache.archiva.repository.LayoutException;
48 import org.apache.archiva.repository.ManagedRepository;
49 import org.apache.archiva.repository.RemoteRepository;
50 import org.apache.archiva.repository.RemoteRepositoryContent;
51 import org.apache.archiva.repository.RepositoryType;
52 import org.apache.archiva.repository.content.Artifact;
53 import org.apache.archiva.repository.content.ItemSelector;
54 import org.apache.archiva.repository.content.base.ArchivaItemSelector;
55 import org.apache.archiva.repository.metadata.base.MetadataTools;
56 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
57 import org.apache.archiva.repository.storage.fs.FilesystemStorage;
58 import org.apache.archiva.repository.storage.fs.FsStorageUtil;
59 import org.apache.archiva.repository.storage.StorageAsset;
60 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
61 import org.apache.archiva.scheduler.repository.model.RepositoryTask;
62 import org.apache.commons.collections4.CollectionUtils;
63 import org.apache.commons.lang3.StringUtils;
64 import org.apache.commons.lang3.SystemUtils;
65 import org.slf4j.Logger;
66 import org.slf4j.LoggerFactory;
67 import org.slf4j.MarkerFactory;
68
69 import javax.annotation.PostConstruct;
70 import javax.inject.Inject;
71 import javax.inject.Named;
72 import java.io.IOException;
73 import java.net.MalformedURLException;
74 import java.nio.file.Files;
75 import java.nio.file.Path;
76 import java.nio.file.StandardCopyOption;
77 import java.util.ArrayList;
78 import java.util.Collections;
79 import java.util.HashMap;
80 import java.util.LinkedHashMap;
81 import java.util.List;
82 import java.util.Map;
83 import java.util.Properties;
84 import java.util.concurrent.ConcurrentHashMap;
85 import java.util.concurrent.ConcurrentMap;
86
87 public abstract class DefaultRepositoryProxyHandler implements RepositoryProxyHandler {
88
89     protected Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyHandler.class );
90     @Inject
91     protected UrlFailureCache urlFailureCache;
92
93     @Inject
94     @Named(value = "metadataTools#default")
95     private MetadataTools metadataTools;
96
97     private Map<String, PreDownloadPolicy> preDownloadPolicies = new HashMap<>(  );
98     private Map<String, PostDownloadPolicy> postDownloadPolicies = new HashMap<>(  );
99     private Map<String, DownloadErrorPolicy> downloadErrorPolicies = new HashMap<>(  );
100     private ConcurrentMap<String, List<ProxyConnector>> proxyConnectorMap = new ConcurrentHashMap<>();
101
102     @Inject
103     @Named(value = "archivaTaskScheduler#repository")
104     private ArchivaTaskScheduler<RepositoryTask> scheduler;
105
106     @Inject
107     private ArchivaConfiguration archivaConfiguration;
108
109     @Inject
110     @Named(value = "fileLockManager#default")
111     private FileLockManager fileLockManager;
112
113     private Map<String, NetworkProxy> networkProxyMap = new ConcurrentHashMap<>();
114     private List<ChecksumAlgorithm> checksumAlgorithms;
115
116     @PostConstruct
117     public void initialize()
118     {
119         checksumAlgorithms = ChecksumUtil.getAlgorithms(archivaConfiguration.getConfiguration().getArchivaRuntimeConfiguration().getChecksumTypes());
120     }
121
122     private List<ProxyConnectorRuleConfiguration> findProxyConnectorRules(String sourceRepository,
123                                                                           String targetRepository,
124                                                                           List<ProxyConnectorRuleConfiguration> all )
125     {
126         List<ProxyConnectorRuleConfiguration> proxyConnectorRuleConfigurations = new ArrayList<>();
127
128         for ( ProxyConnectorRuleConfiguration proxyConnectorRuleConfiguration : all )
129         {
130             for ( ProxyConnectorConfiguration proxyConnector : proxyConnectorRuleConfiguration.getProxyConnectors() )
131             {
132                 if ( StringUtils.equals( sourceRepository, proxyConnector.getSourceRepoId() ) && StringUtils.equals(
133                     targetRepository, proxyConnector.getTargetRepoId() ) )
134                 {
135                     proxyConnectorRuleConfigurations.add( proxyConnectorRuleConfiguration );
136                 }
137             }
138         }
139
140         return proxyConnectorRuleConfigurations;
141     }
142
143
144
145     @Override
146     public StorageAsset fetchFromProxies( ManagedRepository repository, ArtifactReference artifact )
147         throws ProxyDownloadException
148     {
149         StorageAsset localFile = null;
150         Map<String, Exception> previousExceptions = new LinkedHashMap<>();
151         try
152         {
153             localFile = toLocalFile( repository, artifact );
154         }
155         catch ( LayoutException e )
156         {
157             previousExceptions.put( "LayoutException", e );
158             throw new ProxyDownloadException( "Could not convert to BasicRepositoryContentLayout " + e.getMessage( ), previousExceptions);
159         }
160
161         Properties requestProperties = new Properties();
162         requestProperties.setProperty( "filetype", "artifact" );
163         requestProperties.setProperty( "version", artifact.getVersion() );
164         requestProperties.setProperty( "managedRepositoryId", repository.getId() );
165
166         List<ProxyConnector> connectors = getProxyConnectors( repository );
167         for ( ProxyConnector connector : connectors )
168         {
169             if ( !connector.isEnabled() )
170             {
171                 continue;
172             }
173
174             RemoteRepository targetRepository = connector.getTargetRepository();
175             requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
176
177             String targetPath = targetRepository.getContent().toPath( artifact );
178
179             if ( SystemUtils.IS_OS_WINDOWS )
180             {
181                 // toPath use system PATH_SEPARATOR so on windows url are \ which doesn't work very well :-)
182                 targetPath = PathUtil.separatorsToUnix( targetPath );
183             }
184
185             try
186             {
187                 StorageAsset downloadedFile =
188                     transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
189                                   true );
190
191                 if ( fileExists(downloadedFile) )
192                 {
193                     log.debug( "Successfully transferred: {}", downloadedFile.getPath() );
194                     return downloadedFile;
195                 }
196             }
197             catch ( NotFoundException e )
198             {
199                 log.debug( "Artifact {} not found on repository \"{}\".", Keys.toKey( artifact ),
200                            targetRepository.getId() );
201             }
202             catch ( NotModifiedException e )
203             {
204                 log.debug( "Artifact {} not updated on repository \"{}\".", Keys.toKey( artifact ),
205                            targetRepository.getId() );
206             }
207             catch ( ProxyException e )
208             {
209                 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
210                                   targetRepository.getContent(), localFile, e, previousExceptions );
211             }
212         }
213
214         if ( !previousExceptions.isEmpty() )
215         {
216             throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories",
217                                               previousExceptions );
218         }
219
220         log.debug( "Exhausted all target repositories, artifact {} not found.", Keys.toKey( artifact ) );
221
222         return null;
223     }
224
225     @Override
226     public StorageAsset fetchFromProxies( ManagedRepository repository, String path )
227     {
228         StorageAsset localFile = repository.getAsset( path );
229
230         // no update policies for these paths
231         if ( localFile.exists() )
232         {
233             return null;
234         }
235
236         Properties requestProperties = new Properties();
237         requestProperties.setProperty( "filetype", "resource" );
238         requestProperties.setProperty( "managedRepositoryId", repository.getId() );
239
240         List<ProxyConnector> connectors = getProxyConnectors( repository );
241         for ( ProxyConnector connector : connectors )
242         {
243             if ( !connector.isEnabled() )
244             {
245                 continue;
246             }
247
248             RemoteRepository targetRepository = connector.getTargetRepository();
249             requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
250
251             String targetPath = path;
252
253             try
254             {
255                 StorageAsset downloadedFile =
256                     transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
257                                   false );
258
259                 if ( fileExists( downloadedFile ) )
260                 {
261                     log.debug( "Successfully transferred: {}", downloadedFile.getPath() );
262                     return downloadedFile;
263                 }
264             }
265             catch ( NotFoundException e )
266             {
267                 log.debug( "Resource {} not found on repository \"{}\".", path,
268                            targetRepository.getId() );
269             }
270             catch ( NotModifiedException e )
271             {
272                 log.debug( "Resource {} not updated on repository \"{}\".", path,
273                            targetRepository.getId() );
274             }
275             catch ( ProxyException e )
276             {
277                 log.warn(
278                     "Transfer error from repository {} for resource {}, continuing to next repository. Error message: {}",
279                     targetRepository.getId(), path, e.getMessage() );
280                 log.debug( MarkerFactory.getDetachedMarker( "transfer.error" ),
281                            "Transfer error from repository \"{}"
282                                + "\" for resource {}, continuing to next repository. Error message: {}",
283                            targetRepository.getId(), path, e.getMessage(), e );
284             }
285
286         }
287
288         log.debug( "Exhausted all target repositories, resource {} not found.", path );
289
290         return null;
291     }
292
293     @Override
294     public ProxyFetchResult fetchMetadataFromProxies( ManagedRepository repository, String rawLogicalPath )
295     {
296         String logicalPath;
297         if (rawLogicalPath.startsWith( "/" )){
298             logicalPath = rawLogicalPath.substring( 1 );
299         } else {
300             logicalPath = rawLogicalPath;
301         }
302         StorageAsset localFile = repository.getAsset( logicalPath );
303
304         Properties requestProperties = new Properties();
305         requestProperties.setProperty( "filetype", "metadata" );
306         boolean metadataNeedsUpdating = false;
307         long originalTimestamp = getLastModified( localFile );
308
309         List<ProxyConnector> connectors = new ArrayList<>( getProxyConnectors( repository ) );
310         for ( ProxyConnector connector : connectors )
311         {
312             if ( !connector.isEnabled() )
313             {
314                 continue;
315             }
316
317             RemoteRepository targetRepository = connector.getTargetRepository();
318
319             StorageAsset localRepoFile = toLocalRepoFile( repository, targetRepository.getContent(), logicalPath );
320             long originalMetadataTimestamp = getLastModified( localRepoFile );
321
322             try
323             {
324                 transferFile( connector, targetRepository, logicalPath, repository, localRepoFile, requestProperties,
325                               true );
326
327                 if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
328                 {
329                     metadataNeedsUpdating = true;
330                 }
331             }
332             catch ( NotFoundException e )
333             {
334
335                 log.debug( "Metadata {} not found on remote repository '{}'.", logicalPath,
336                            targetRepository.getId(), e );
337
338             }
339             catch ( NotModifiedException e )
340             {
341
342                 log.debug( "Metadata {} not updated on remote repository '{}'.", logicalPath,
343                            targetRepository.getId(), e );
344
345             }
346             catch ( ProxyException e )
347             {
348                 log.warn(
349                     "Transfer error from repository {} for versioned Metadata {}, continuing to next repository. Error message: {}",
350                     targetRepository.getId(), logicalPath, e.getMessage() );
351                 log.debug( "Full stack trace", e );
352             }
353         }
354
355         if ( hasBeenUpdated( localFile, originalTimestamp ) )
356         {
357             metadataNeedsUpdating = true;
358         }
359
360         if ( metadataNeedsUpdating || !localFile.exists())
361         {
362             try
363             {
364                 metadataTools.updateMetadata( repository.getContent(), logicalPath );
365             }
366             catch ( RepositoryMetadataException e )
367             {
368                 log.warn( "Unable to update metadata {}:{}", localFile.getPath(), e.getMessage(), e );
369             }
370
371         }
372
373         if ( fileExists( localFile ) )
374         {
375             return new ProxyFetchResult( localFile, metadataNeedsUpdating );
376         }
377
378         return new ProxyFetchResult( null, false );
379     }
380
381     private long getLastModified(StorageAsset file )
382     {
383         if ( !file.exists() || file.isContainer() )
384         {
385             return 0;
386         }
387
388         return file.getModificationTime().toEpochMilli();
389     }
390
391     private boolean hasBeenUpdated(StorageAsset file, long originalLastModified )
392     {
393         if ( !file.exists() || file.isContainer() )
394         {
395             return false;
396         }
397
398         long currentLastModified = getLastModified( file );
399         return ( currentLastModified > originalLastModified );
400     }
401
402     private StorageAsset toLocalRepoFile( ManagedRepository repository, RemoteRepositoryContent targetRepository,
403                                           String targetPath )
404     {
405         String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath );
406         return repository.getAsset( repoPath );
407     }
408
409     /**
410      * Test if the provided ManagedRepositoryContent has any proxies configured for it.
411      * @param repository
412      */
413     @Override
414     public boolean hasProxies( ManagedRepository repository )
415     {
416         synchronized ( this.proxyConnectorMap )
417         {
418             return this.proxyConnectorMap.containsKey( repository.getId() );
419         }
420     }
421
422     private StorageAsset toLocalFile(ManagedRepository repository, ArtifactReference artifact ) throws LayoutException
423     {
424         ItemSelector selector = ArchivaItemSelector.builder( )
425             .withNamespace( artifact.getGroupId( ) )
426             .withProjectId( artifact.getArtifactId( ) )
427             .withArtifactId( artifact.getArtifactId( ) )
428             .withArtifactVersion( artifact.getVersion() )
429             .withVersion( artifact.getProjectVersion( ) )
430             .withType( artifact.getType( ) ).build();
431         Artifact repoArtifact = repository.getContent( ).getLayout( BaseRepositoryContentLayout.class ).getArtifact( selector );
432         return repoArtifact.getAsset( );
433     }
434
435     /**
436      * Simple method to test if the file exists on the local disk.
437      *
438      * @param file the file to test. (may be null)
439      * @return true if file exists. false if the file param is null, doesn't exist, or is not of type File.
440      */
441     private boolean fileExists( StorageAsset file )
442     {
443         if ( file == null )
444         {
445             return false;
446         }
447
448         if ( !file.exists())
449         {
450             return false;
451         }
452
453         return !file.isContainer();
454     }
455
456     /**
457      * Perform the transfer of the file.
458      *
459      * @param connector         the connector configuration to use.
460      * @param remoteRepository  the remote repository get the resource from.
461      * @param remotePath        the path in the remote repository to the resource to get.
462      * @param repository        the managed repository that will hold the file
463      * @param resource          the path relative to the repository storage where the file should be downloaded to
464      * @param requestProperties the request properties to utilize for policy handling.
465      * @param executeConsumers  whether to execute the consumers after proxying
466      * @return the local file that was downloaded, or null if not downloaded.
467      * @throws NotFoundException    if the file was not found on the remote repository.
468      * @throws NotModifiedException if the localFile was present, and the resource was present on remote repository, but
469      *                              the remote resource is not newer than the local File.
470      * @throws ProxyException       if transfer was unsuccessful.
471      */
472     protected StorageAsset transferFile( ProxyConnector connector, RemoteRepository remoteRepository, String remotePath,
473                                          ManagedRepository repository, StorageAsset resource, Properties requestProperties,
474                                          boolean executeConsumers )
475         throws ProxyException, NotModifiedException
476     {
477         String url = null;
478         try
479         {
480             url = remoteRepository.getLocation().toURL().toString();
481         }
482         catch ( MalformedURLException e )
483         {
484             throw new ProxyException( e.getMessage(), e );
485         }
486         if ( !url.endsWith( "/" ) )
487         {
488             url = url + "/";
489         }
490         if (remotePath.startsWith( "/" )) {
491             url = url + remotePath.substring( 1 );
492         } else {
493             url = url + remotePath;
494         }
495         requestProperties.setProperty( "url", url );
496
497         // Is a whitelist defined?
498         if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
499         {
500             // Path must belong to whitelist.
501             if ( !matchesPattern( remotePath, connector.getWhitelist() ) )
502             {
503                 log.debug( "Path [{}] is not part of defined whitelist (skipping transfer from repository [{}]).",
504                            remotePath, remoteRepository.getId() );
505                 return null;
506             }
507         }
508
509         // Is target path part of blacklist?
510         if ( matchesPattern( remotePath, connector.getBlacklist() ) )
511         {
512             log.debug( "Path [{}] is part of blacklist (skipping transfer from repository [{}]).", remotePath,
513                        remoteRepository.getId() );
514             return null;
515         }
516
517         // Handle pre-download policy
518         try
519         {
520             validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, resource );
521         }
522         catch ( PolicyViolationException e )
523         {
524             String emsg = "Transfer not attempted on " + url + " : " + e.getMessage();
525             if ( resource.exists() )
526             {
527                 log.debug( "{} : using already present local file.", emsg );
528                 return resource;
529             }
530
531             log.debug( emsg );
532             return null;
533         }
534
535         Path workingDirectory = createWorkingDirectory( repository );
536         FilesystemStorage tmpStorage = null;
537         try
538         {
539             tmpStorage = new FilesystemStorage( workingDirectory, fileLockManager );
540         }
541         catch ( IOException e )
542         {
543             throw new ProxyException( "Could not create tmp storage" );
544         }
545         StorageAsset tmpResource = tmpStorage.getAsset( resource.getName( ) );
546         StorageAsset[] tmpChecksumFiles = new StorageAsset[checksumAlgorithms.size()];
547         for(int i=0; i<checksumAlgorithms.size(); i++) {
548             ChecksumAlgorithm alg = checksumAlgorithms.get( i );
549             tmpChecksumFiles[i] = tmpStorage.getAsset( resource.getName() + "." + alg.getDefaultExtension() );
550         }
551
552         try
553         {
554
555             transferResources( connector, remoteRepository, tmpResource,tmpChecksumFiles , url, remotePath,
556                 resource, workingDirectory, repository );
557
558             // Handle post-download policies.
559             try
560             {
561                 validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, tmpResource );
562             }
563             catch ( PolicyViolationException e )
564             {
565                 log.warn( "Transfer invalidated from {} : {}", url, e.getMessage() );
566                 executeConsumers = false;
567                 if ( !fileExists( tmpResource ) )
568                 {
569                     resource = null;
570                 }
571             }
572
573             if ( resource != null )
574             {
575                 synchronized ( resource.getPath().intern() )
576                 {
577                     StorageAsset directory = resource.getParent();
578                     for (int i=0; i<tmpChecksumFiles.length; i++) {
579                         moveFileIfExists( tmpChecksumFiles[i], directory );
580                     }
581                     moveFileIfExists( tmpResource, directory );
582                 }
583             }
584         }
585         finally
586         {
587             org.apache.archiva.common.utils.FileUtils.deleteQuietly( workingDirectory );
588         }
589
590         if ( executeConsumers )
591         {
592             // Just-in-time update of the index and database by executing the consumers for this artifact
593             //consumers.executeConsumers( connector.getSourceRepository().getRepository(), resource );
594             queueRepositoryTask( connector.getSourceRepository().getId(), resource );
595         }
596
597         return resource;
598     }
599
600     protected abstract void transferResources( ProxyConnector connector, RemoteRepository remoteRepository,
601                                                StorageAsset tmpResource, StorageAsset[] checksumFiles, String url, String remotePath, StorageAsset resource, Path workingDirectory,
602                                                ManagedRepository repository ) throws ProxyException;
603
604     private void queueRepositoryTask(String repositoryId, StorageAsset localFile )
605     {
606         RepositoryTask task = new RepositoryTask();
607         task.setRepositoryId( repositoryId );
608         task.setResourceFile( localFile );
609         task.setUpdateRelatedArtifacts( true );
610         task.setScanAll( true );
611
612         try
613         {
614             scheduler.queueTask( task );
615         }
616         catch ( TaskQueueException e )
617         {
618             log.error( "Unable to queue repository task to execute consumers on resource file ['{}"
619                            + "'].", localFile.getName() );
620         }
621     }
622
623     /**
624      * Moves the file into repository location if it exists
625      *
626      * @param fileToMove this could be either the main artifact, sha1 or md5 checksum file.
627      * @param directory  directory to write files to
628      */
629     private void moveFileIfExists( StorageAsset fileToMove, StorageAsset directory )
630         throws ProxyException
631     {
632         if ( fileToMove != null && fileToMove.exists() )
633         {
634             StorageAsset newLocation = directory.getStorage().getAsset( directory.getPath()+ "/" + fileToMove.getName());
635             moveTempToTarget( fileToMove, newLocation );
636         }
637     }
638
639     /**
640      * Apply the policies.
641      *
642      * @param policies  the map of policies to execute. (Map of String policy keys, to {@link DownloadPolicy} objects)
643      * @param settings  the map of settings for the policies to execute. (Map of String policy keys, to String policy
644      *                  setting)
645      * @param request   the request properties (utilized by the {@link DownloadPolicy#applyPolicy(PolicyOption, Properties, StorageAsset)}
646      *                  )
647      * @param localFile the local file (utilized by the {@link DownloadPolicy#applyPolicy(PolicyOption, Properties, StorageAsset)})
648      * @throws PolicyViolationException
649      */
650     private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<Policy, PolicyOption> settings,
651                                    Properties request, StorageAsset localFile )
652         throws PolicyViolationException
653     {
654         for ( Map.Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
655         {
656             // olamy with spring rolehint is now downloadPolicy#hint
657             // so substring after last # to get the hint as with plexus
658             String key = entry.getValue( ).getId( );
659             DownloadPolicy policy = entry.getValue();
660             PolicyOption option = settings.containsKey(policy ) ? settings.get(policy) : policy.getDefaultOption();
661
662             log.debug( "Applying [{}] policy with [{}]", key, option );
663             try
664             {
665                 policy.applyPolicy( option, request, localFile );
666             }
667             catch ( PolicyConfigurationException e )
668             {
669                 log.error( e.getMessage(), e );
670             }
671         }
672     }
673
674     private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<Policy, PolicyOption> settings,
675                                    Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
676                                    StorageAsset localFile, Exception exception, Map<String, Exception> previousExceptions )
677         throws ProxyDownloadException
678     {
679         boolean process = true;
680         for ( Map.Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
681         {
682
683             // olamy with spring rolehint is now downloadPolicy#hint
684             // so substring after last # to get the hint as with plexus
685             String key = entry.getValue( ).getId( );
686             DownloadErrorPolicy policy = entry.getValue();
687             PolicyOption option = settings.containsKey( policy ) ? settings.get(policy) : policy.getDefaultOption();
688
689             log.debug( "Applying [{}] policy with [{}]", key, option );
690             try
691             {
692                 // all policies must approve the exception, any can cancel
693                 process = policy.applyPolicy( option, request, localFile, exception, previousExceptions );
694                 if ( !process )
695                 {
696                     break;
697                 }
698             }
699             catch ( PolicyConfigurationException e )
700             {
701                 log.error( e.getMessage(), e );
702             }
703         }
704
705         if ( process )
706         {
707             // if the exception was queued, don't throw it
708             if ( !previousExceptions.containsKey( content.getId() ) )
709             {
710                 throw new ProxyDownloadException(
711                     "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
712                     content.getId(), exception );
713             }
714         }
715         else
716         {
717             // if the exception was queued, but cancelled, remove it
718             previousExceptions.remove( content.getId() );
719         }
720
721         log.warn(
722             "Transfer error from repository {} for artifact {} , continuing to next repository. Error message: {}",
723             content.getRepository().getId(), Keys.toKey( artifact ), exception.getMessage() );
724         log.debug( "Full stack trace", exception );
725     }
726
727     /**
728      * Creates a working directory
729      *
730      * @param repository
731      * @return file location of working directory
732      */
733     private Path createWorkingDirectory( ManagedRepository repository )
734     {
735         try
736         {
737             return Files.createTempDirectory( "temp" );
738         }
739         catch ( IOException e )
740         {
741             throw new RuntimeException( e.getMessage(), e );
742         }
743
744     }
745
746     /**
747      * Used to move the temporary file to its real destination. This is patterned from the way WagonManager handles its
748      * downloaded files.
749      *
750      * @param temp   The completed download file
751      * @param target The final location of the downloaded file
752      * @throws ProxyException when the temp file cannot replace the target file
753      */
754     private void moveTempToTarget( StorageAsset temp, StorageAsset target )
755         throws ProxyException
756     {
757
758         try
759         {
760             org.apache.archiva.repository.storage.util.StorageUtil.moveAsset( temp, target, true , StandardCopyOption.REPLACE_EXISTING);
761         }
762         catch ( IOException e )
763         {
764             log.error( "Move failed from {} to {}, trying copy.", temp, target );
765             try
766             {
767                 FsStorageUtil.copyAsset( temp, target, true );
768                 if (temp.exists()) {
769                     temp.getStorage( ).removeAsset( temp );
770                 }
771             }
772             catch ( IOException ex )
773             {
774                 log.error("Copy failed from {} to {}: ({}) {}", temp, target, e.getClass(), e.getMessage());
775                 throw new ProxyException("Could not move temp file "+temp.getPath()+" to target "+target.getPath()+": ("+e.getClass()+") "+e.getMessage(), e);
776             }
777         }
778     }
779
780     /**
781      * Tests whitelist and blacklist patterns against path.
782      *
783      * @param path     the path to test.
784      * @param patterns the list of patterns to check.
785      * @return true if the path matches at least 1 pattern in the provided patterns list.
786      */
787     private boolean matchesPattern( String path, List<String> patterns )
788     {
789         if ( CollectionUtils.isEmpty( patterns ) )
790         {
791             return false;
792         }
793
794         if ( !path.startsWith( "/" ) )
795         {
796             path = "/" + path;
797         }
798
799         for ( String pattern : patterns )
800         {
801             if ( !pattern.startsWith( "/" ) )
802             {
803                 pattern = "/" + pattern;
804             }
805
806             if ( PathUtil.matchPath( pattern, path, false ) )
807             {
808                 return true;
809             }
810         }
811
812         return false;
813     }
814
815     /**
816      * TODO: Ensure that list is correctly ordered based on configuration. See MRM-477
817      * @param repository
818      */
819     @Override
820     public List<ProxyConnector> getProxyConnectors( ManagedRepository repository )
821     {
822
823         if ( !this.proxyConnectorMap.containsKey( repository.getId() ) )
824         {
825             return Collections.emptyList();
826         }
827         List<ProxyConnector> ret = new ArrayList<>( this.proxyConnectorMap.get( repository.getId() ) );
828
829         Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
830         return ret;
831
832     }
833
834
835     protected String addParameters(String path, RemoteRepository remoteRepository )
836     {
837         if ( remoteRepository.getExtraParameters().isEmpty() )
838         {
839             return path;
840         }
841
842         boolean question = false;
843
844         StringBuilder res = new StringBuilder( path == null ? "" : path );
845
846         for ( Map.Entry<String, String> entry : remoteRepository.getExtraParameters().entrySet() )
847         {
848             if ( !question )
849             {
850                 res.append( '?' ).append( entry.getKey() ).append( '=' ).append( entry.getValue() );
851             }
852         }
853
854         return res.toString();
855     }
856
857     public void setArchivaConfiguration(ArchivaConfiguration archivaConfiguration )
858     {
859         this.archivaConfiguration = archivaConfiguration;
860     }
861
862     public MetadataTools getMetadataTools()
863     {
864         return metadataTools;
865     }
866
867     public void setMetadataTools(MetadataTools metadataTools )
868     {
869         this.metadataTools = metadataTools;
870     }
871
872     public UrlFailureCache getUrlFailureCache()
873     {
874         return urlFailureCache;
875     }
876
877     public void setUrlFailureCache(UrlFailureCache urlFailureCache )
878     {
879         this.urlFailureCache = urlFailureCache;
880     }
881
882     public Map<String, PreDownloadPolicy> getPreDownloadPolicies()
883     {
884         return preDownloadPolicies;
885     }
886
887     public void setPreDownloadPolicies(Map<String, PreDownloadPolicy> preDownloadPolicies )
888     {
889         this.preDownloadPolicies = preDownloadPolicies;
890     }
891
892     public Map<String, PostDownloadPolicy> getPostDownloadPolicies()
893     {
894         return postDownloadPolicies;
895     }
896
897     public void setPostDownloadPolicies(Map<String, PostDownloadPolicy> postDownloadPolicies )
898     {
899         this.postDownloadPolicies = postDownloadPolicies;
900     }
901
902     public Map<String, DownloadErrorPolicy> getDownloadErrorPolicies()
903     {
904         return downloadErrorPolicies;
905     }
906
907     public void setDownloadErrorPolicies(Map<String, DownloadErrorPolicy> downloadErrorPolicies )
908     {
909         this.downloadErrorPolicies = downloadErrorPolicies;
910     }
911
912     @Override
913     public void setNetworkProxies(Map<String, NetworkProxy> networkProxies ) {
914         this.networkProxyMap.clear();
915         this.networkProxyMap.putAll( networkProxies );
916     }
917
918     @Override
919     public NetworkProxy getNetworkProxy(String id) {
920         return this.networkProxyMap.get(id);
921     }
922
923     @Override
924     public Map<String, NetworkProxy> getNetworkProxies() {
925         return this.networkProxyMap;
926     }
927
928     @Override
929     public abstract List<RepositoryType> supports();
930
931     @Override
932     public void setPolicies( List<Policy> policyList )
933     {
934         preDownloadPolicies.clear();
935         postDownloadPolicies.clear();
936         downloadErrorPolicies.clear();
937         for (Policy policy : policyList) {
938             addPolicy( policy );
939         }
940     }
941
942     void addPolicy(PreDownloadPolicy policy) {
943         preDownloadPolicies.put( policy.getId( ), policy );
944     }
945
946     void addPolicy(PostDownloadPolicy policy) {
947         postDownloadPolicies.put( policy.getId( ), policy );
948     }
949     void addPolicy(DownloadErrorPolicy policy) {
950         downloadErrorPolicies.put( policy.getId( ), policy );
951     }
952
953     @Override
954     public void addPolicy( Policy policy )
955     {
956         if (policy instanceof PreDownloadPolicy) {
957             addPolicy( (PreDownloadPolicy)policy );
958         } else if (policy instanceof PostDownloadPolicy) {
959             addPolicy( (PostDownloadPolicy) policy );
960         } else if (policy instanceof DownloadErrorPolicy) {
961             addPolicy( (DownloadErrorPolicy) policy );
962         } else {
963             log.warn( "Policy not known: {}, {}", policy.getId( ), policy.getClass( ).getName( ) );
964         }
965     }
966
967     @Override
968     public void removePolicy( Policy policy )
969     {
970         final String id = policy.getId();
971         if (preDownloadPolicies.containsKey( id )) {
972             preDownloadPolicies.remove( id );
973         } else if (postDownloadPolicies.containsKey( id )) {
974             postDownloadPolicies.remove( id );
975         } else if (downloadErrorPolicies.containsKey( id )) {
976             downloadErrorPolicies.remove( id );
977         }
978     }
979
980     @Override
981     public void addProxyConnector( ProxyConnector connector )
982     {
983         final String sourceId = connector.getSourceRepository( ).getId( );
984         List<ProxyConnector> connectors;
985         if (proxyConnectorMap.containsKey( sourceId )) {
986             connectors = proxyConnectorMap.get( sourceId );
987         } else {
988             connectors = new ArrayList<>( );
989             proxyConnectorMap.put( sourceId, connectors );
990         }
991         connectors.add( connector );
992     }
993
994     @Override
995     public void setProxyConnectors( List<ProxyConnector> proxyConnectors )
996     {
997         proxyConnectorMap.clear();
998         for ( ProxyConnector connector : proxyConnectors )
999         {
1000             addProxyConnector( connector );
1001         }
1002     }
1003 }