This project has retired. For details please refer to its
Attic page.
DefaultRepositoryProxyConnectors xref
1 package org.apache.archiva.proxy;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.archiva.admin.model.RepositoryAdminException;
23 import org.apache.archiva.admin.model.beans.NetworkProxy;
24 import org.apache.archiva.admin.model.beans.ProxyConnectorRuleType;
25 import org.apache.archiva.admin.model.beans.RemoteRepository;
26 import org.apache.archiva.admin.model.networkproxy.NetworkProxyAdmin;
27 import org.apache.archiva.common.filelock.FileLockException;
28 import org.apache.archiva.common.filelock.FileLockManager;
29 import org.apache.archiva.common.filelock.FileLockTimeoutException;
30 import org.apache.archiva.common.filelock.Lock;
31 import org.apache.archiva.configuration.ArchivaConfiguration;
32 import org.apache.archiva.configuration.Configuration;
33 import org.apache.archiva.configuration.ConfigurationNames;
34 import org.apache.archiva.configuration.NetworkProxyConfiguration;
35 import org.apache.archiva.configuration.ProxyConnectorConfiguration;
36 import org.apache.archiva.configuration.ProxyConnectorRuleConfiguration;
37 import org.apache.archiva.model.ArtifactReference;
38 import org.apache.archiva.model.Keys;
39 import org.apache.archiva.model.RepositoryURL;
40 import org.apache.archiva.policies.DownloadErrorPolicy;
41 import org.apache.archiva.policies.DownloadPolicy;
42 import org.apache.archiva.policies.PolicyConfigurationException;
43 import org.apache.archiva.policies.PolicyViolationException;
44 import org.apache.archiva.policies.PostDownloadPolicy;
45 import org.apache.archiva.policies.PreDownloadPolicy;
46 import org.apache.archiva.policies.ProxyDownloadException;
47 import org.apache.archiva.policies.urlcache.UrlFailureCache;
48 import org.apache.archiva.proxy.common.WagonFactory;
49 import org.apache.archiva.proxy.common.WagonFactoryException;
50 import org.apache.archiva.proxy.common.WagonFactoryRequest;
51 import org.apache.archiva.proxy.model.ProxyConnector;
52 import org.apache.archiva.proxy.model.ProxyFetchResult;
53 import org.apache.archiva.proxy.model.RepositoryProxyConnectors;
54 import org.apache.archiva.redback.components.registry.Registry;
55 import org.apache.archiva.redback.components.registry.RegistryListener;
56 import org.apache.archiva.redback.components.taskqueue.TaskQueueException;
57 import org.apache.archiva.repository.ManagedRepositoryContent;
58 import org.apache.archiva.repository.RemoteRepositoryContent;
59 import org.apache.archiva.repository.RepositoryContentFactory;
60 import org.apache.archiva.repository.RepositoryException;
61 import org.apache.archiva.repository.RepositoryNotFoundException;
62 import org.apache.archiva.repository.metadata.MetadataTools;
63 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
64 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
65 import org.apache.archiva.scheduler.repository.model.RepositoryTask;
66 import org.apache.commons.collections.CollectionUtils;
67 import org.apache.commons.io.FileUtils;
68 import org.apache.commons.io.FilenameUtils;
69 import org.apache.commons.lang.StringUtils;
70 import org.apache.commons.lang.SystemUtils;
71 import org.apache.maven.wagon.ConnectionException;
72 import org.apache.maven.wagon.ResourceDoesNotExistException;
73 import org.apache.maven.wagon.Wagon;
74 import org.apache.maven.wagon.WagonException;
75 import org.apache.maven.wagon.authentication.AuthenticationException;
76 import org.apache.maven.wagon.authentication.AuthenticationInfo;
77 import org.apache.maven.wagon.proxy.ProxyInfo;
78 import org.apache.maven.wagon.repository.Repository;
79 import org.apache.tools.ant.types.selectors.SelectorUtils;
80 import org.slf4j.Logger;
81 import org.slf4j.LoggerFactory;
82 import org.slf4j.MarkerFactory;
83 import org.springframework.stereotype.Service;
84
85 import javax.annotation.PostConstruct;
86 import javax.inject.Inject;
87 import javax.inject.Named;
88 import java.io.File;
89 import java.io.IOException;
90 import java.nio.file.Files;
91 import java.util.ArrayList;
92 import java.util.Collections;
93 import java.util.LinkedHashMap;
94 import java.util.List;
95 import java.util.Map;
96 import java.util.Map.Entry;
97 import java.util.Properties;
98 import java.util.concurrent.ConcurrentHashMap;
99 import java.util.concurrent.ConcurrentMap;
100 import java.util.concurrent.TimeUnit;
101
102
103
104
105
106
107 @Service("repositoryProxyConnectors#default")
108 public class DefaultRepositoryProxyConnectors
109 implements RepositoryProxyConnectors, RegistryListener
110 {
111 private Logger log = LoggerFactory.getLogger( DefaultRepositoryProxyConnectors.class );
112
113 @Inject
114 @Named(value = "archivaConfiguration#default")
115 private ArchivaConfiguration archivaConfiguration;
116
117 @Inject
118 @Named(value = "repositoryContentFactory#default")
119 private RepositoryContentFactory repositoryFactory;
120
121 @Inject
122 @Named(value = "metadataTools#default")
123 private MetadataTools metadataTools;
124
125 @Inject
126 private Map<String, PreDownloadPolicy> preDownloadPolicies;
127
128 @Inject
129 private Map<String, PostDownloadPolicy> postDownloadPolicies;
130
131 @Inject
132 private Map<String, DownloadErrorPolicy> downloadErrorPolicies;
133
134 @Inject
135 private UrlFailureCache urlFailureCache;
136
137 private ConcurrentMap<String, List<ProxyConnector>> proxyConnectorMap = new ConcurrentHashMap<>();
138
139 private ConcurrentMap<String, ProxyInfo> networkProxyMap = new ConcurrentHashMap<>();
140
141 @Inject
142 private WagonFactory wagonFactory;
143
144 @Inject
145 @Named(value = "archivaTaskScheduler#repository")
146 private ArchivaTaskScheduler scheduler;
147
148 @Inject
149 private NetworkProxyAdmin networkProxyAdmin;
150
151 @Inject
152 @Named(value = "fileLockManager#default")
153 private FileLockManager fileLockManager;
154
155 @PostConstruct
156 public void initialize()
157 {
158 initConnectorsAndNetworkProxies();
159 archivaConfiguration.addChangeListener( this );
160
161 }
162
163 @SuppressWarnings("unchecked")
164 private void initConnectorsAndNetworkProxies()
165 {
166
167 ProxyConnectorOrderComparator proxyOrderSorter = new ProxyConnectorOrderComparator();
168 this.proxyConnectorMap.clear();
169
170 Configuration configuration = archivaConfiguration.getConfiguration();
171
172 List<ProxyConnectorRuleConfiguration> allProxyConnectorRuleConfigurations =
173 configuration.getProxyConnectorRuleConfigurations();
174
175 List<ProxyConnectorConfiguration> proxyConfigs = configuration.getProxyConnectors();
176 for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs )
177 {
178 String key = proxyConfig.getSourceRepoId();
179
180 try
181 {
182
183 ProxyConnector connector = new ProxyConnector();
184
185 connector.setSourceRepository(
186 repositoryFactory.getManagedRepositoryContent( proxyConfig.getSourceRepoId() ) );
187 connector.setTargetRepository(
188 repositoryFactory.getRemoteRepositoryContent( proxyConfig.getTargetRepoId() ) );
189
190 connector.setProxyId( proxyConfig.getProxyId() );
191 connector.setPolicies( proxyConfig.getPolicies() );
192 connector.setOrder( proxyConfig.getOrder() );
193 connector.setDisabled( proxyConfig.isDisabled() );
194
195
196 List<String> blacklist = new ArrayList<>( 0 );
197 if ( CollectionUtils.isNotEmpty( proxyConfig.getBlackListPatterns() ) )
198 {
199 blacklist.addAll( proxyConfig.getBlackListPatterns() );
200 }
201 connector.setBlacklist( blacklist );
202
203
204 List<String> whitelist = new ArrayList<>( 0 );
205 if ( CollectionUtils.isNotEmpty( proxyConfig.getWhiteListPatterns() ) )
206 {
207 whitelist.addAll( proxyConfig.getWhiteListPatterns() );
208 }
209 connector.setWhitelist( whitelist );
210
211 List<ProxyConnectorRuleConfiguration> proxyConnectorRuleConfigurations =
212 findProxyConnectorRules( connector.getSourceRepository().getId(),
213 connector.getTargetRepository().getId(),
214 allProxyConnectorRuleConfigurations );
215
216 if ( !proxyConnectorRuleConfigurations.isEmpty() )
217 {
218 for ( ProxyConnectorRuleConfiguration proxyConnectorRuleConfiguration : proxyConnectorRuleConfigurations )
219 {
220 if ( StringUtils.equals( proxyConnectorRuleConfiguration.getRuleType(),
221 ProxyConnectorRuleType.BLACK_LIST.getRuleType() ) )
222 {
223 connector.getBlacklist().add( proxyConnectorRuleConfiguration.getPattern() );
224 }
225
226 if ( StringUtils.equals( proxyConnectorRuleConfiguration.getRuleType(),
227 ProxyConnectorRuleType.WHITE_LIST.getRuleType() ) )
228 {
229 connector.getWhitelist().add( proxyConnectorRuleConfiguration.getPattern() );
230 }
231 }
232 }
233
234
235 List<ProxyConnector> connectors = this.proxyConnectorMap.get( key );
236 if ( connectors == null )
237 {
238
239 connectors = new ArrayList<>( 1 );
240 }
241
242
243 connectors.add( connector );
244
245
246 Collections.sort( connectors, proxyOrderSorter );
247
248
249 this.proxyConnectorMap.put( key, connectors );
250 }
251 catch ( RepositoryNotFoundException e )
252 {
253 log.warn( "Unable to use proxy connector: {}", e.getMessage(), e );
254 }
255 catch ( RepositoryException e )
256 {
257 log.warn( "Unable to use proxy connector: {}", e.getMessage(), e );
258 }
259
260
261 }
262
263 this.networkProxyMap.clear();
264
265 List<NetworkProxyConfiguration> networkProxies = archivaConfiguration.getConfiguration().getNetworkProxies();
266 for ( NetworkProxyConfiguration networkProxyConfig : networkProxies )
267 {
268 String key = networkProxyConfig.getId();
269
270 ProxyInfo proxy = new ProxyInfo();
271
272 proxy.setType( networkProxyConfig.getProtocol() );
273 proxy.setHost( networkProxyConfig.getHost() );
274 proxy.setPort( networkProxyConfig.getPort() );
275 proxy.setUserName( networkProxyConfig.getUsername() );
276 proxy.setPassword( networkProxyConfig.getPassword() );
277
278 this.networkProxyMap.put( key, proxy );
279 }
280
281 }
282
283 private List<ProxyConnectorRuleConfiguration> findProxyConnectorRules( String sourceRepository,
284 String targetRepository,
285 List<ProxyConnectorRuleConfiguration> all )
286 {
287 List<ProxyConnectorRuleConfiguration> proxyConnectorRuleConfigurations = new ArrayList<>();
288
289 for ( ProxyConnectorRuleConfiguration proxyConnectorRuleConfiguration : all )
290 {
291 for ( ProxyConnectorConfiguration proxyConnector : proxyConnectorRuleConfiguration.getProxyConnectors() )
292 {
293 if ( StringUtils.equals( sourceRepository, proxyConnector.getSourceRepoId() ) && StringUtils.equals(
294 targetRepository, proxyConnector.getTargetRepoId() ) )
295 {
296 proxyConnectorRuleConfigurations.add( proxyConnectorRuleConfiguration );
297 }
298 }
299 }
300
301 return proxyConnectorRuleConfigurations;
302 }
303
304 @Override
305 public File fetchFromProxies( ManagedRepositoryContent repository, ArtifactReference artifact )
306 throws ProxyDownloadException
307 {
308 File localFile = toLocalFile( repository, artifact );
309
310 Properties requestProperties = new Properties();
311 requestProperties.setProperty( "filetype", "artifact" );
312 requestProperties.setProperty( "version", artifact.getVersion() );
313 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
314
315 List<ProxyConnector> connectors = getProxyConnectors( repository );
316 Map<String, Exception> previousExceptions = new LinkedHashMap<>();
317 for ( ProxyConnector connector : connectors )
318 {
319 if ( connector.isDisabled() )
320 {
321 continue;
322 }
323
324 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
325 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
326
327 String targetPath = targetRepository.toPath( artifact );
328
329 if ( SystemUtils.IS_OS_WINDOWS )
330 {
331
332 targetPath = FilenameUtils.separatorsToUnix( targetPath );
333 }
334
335 try
336 {
337 File downloadedFile =
338 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
339 true );
340
341 if ( fileExists( downloadedFile ) )
342 {
343 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
344 return downloadedFile;
345 }
346 }
347 catch ( NotFoundException e )
348 {
349 log.debug( "Artifact {} not found on repository \"{}\".", Keys.toKey( artifact ),
350 targetRepository.getRepository().getId() );
351 }
352 catch ( NotModifiedException e )
353 {
354 log.debug( "Artifact {} not updated on repository \"{}\".", Keys.toKey( artifact ),
355 targetRepository.getRepository().getId() );
356 }
357 catch ( ProxyException | RepositoryAdminException e )
358 {
359 validatePolicies( this.downloadErrorPolicies, connector.getPolicies(), requestProperties, artifact,
360 targetRepository, localFile, e, previousExceptions );
361 }
362 }
363
364 if ( !previousExceptions.isEmpty() )
365 {
366 throw new ProxyDownloadException( "Failures occurred downloading from some remote repositories",
367 previousExceptions );
368 }
369
370 log.debug( "Exhausted all target repositories, artifact {} not found.", Keys.toKey( artifact ) );
371
372 return null;
373 }
374
375 @Override
376 public File fetchFromProxies( ManagedRepositoryContent repository, String path )
377 {
378 File localFile = new File( repository.getRepoRoot(), path );
379
380
381 if ( localFile.exists() )
382 {
383 return null;
384 }
385
386 Properties requestProperties = new Properties();
387 requestProperties.setProperty( "filetype", "resource" );
388 requestProperties.setProperty( "managedRepositoryId", repository.getId() );
389
390 List<ProxyConnector> connectors = getProxyConnectors( repository );
391 for ( ProxyConnector connector : connectors )
392 {
393 if ( connector.isDisabled() )
394 {
395 continue;
396 }
397
398 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
399 requestProperties.setProperty( "remoteRepositoryId", targetRepository.getId() );
400
401 String targetPath = path;
402
403 try
404 {
405 File downloadedFile =
406 transferFile( connector, targetRepository, targetPath, repository, localFile, requestProperties,
407 false );
408
409 if ( fileExists( downloadedFile ) )
410 {
411 log.debug( "Successfully transferred: {}", downloadedFile.getAbsolutePath() );
412 return downloadedFile;
413 }
414 }
415 catch ( NotFoundException e )
416 {
417 log.debug( "Resource {} not found on repository \"{}\".", path,
418 targetRepository.getRepository().getId() );
419 }
420 catch ( NotModifiedException e )
421 {
422 log.debug( "Resource {} not updated on repository \"{}\".", path,
423 targetRepository.getRepository().getId() );
424 }
425 catch ( ProxyException e )
426 {
427 log.warn(
428 "Transfer error from repository {} for resource {}, continuing to next repository. Error message: {}",
429 targetRepository.getRepository().getId(), path, e.getMessage() );
430 log.debug( MarkerFactory.getDetachedMarker( "transfer.error" ),
431 "Transfer error from repository \"" + targetRepository.getRepository().getId()
432 + "\" for resource " + path + ", continuing to next repository. Error message: {}",
433 e.getMessage(), e
434 );
435 }
436 catch ( RepositoryAdminException e )
437 {
438 log.debug( MarkerFactory.getDetachedMarker( "transfer.error" ),
439 "Transfer error from repository {} for resource {}, continuing to next repository. Error message: {}",
440 targetRepository.getRepository().getId(), path, e.getMessage(), e );
441 log.debug( MarkerFactory.getDetachedMarker( "transfer.error" ), "Full stack trace", e );
442 }
443 }
444
445 log.debug( "Exhausted all target repositories, resource {} not found.", path );
446
447 return null;
448 }
449
450 @Override
451 public ProxyFetchResult fetchMetadataFromProxies( ManagedRepositoryContent repository, String logicalPath )
452 {
453 File localFile = new File( repository.getRepoRoot(), logicalPath );
454
455 Properties requestProperties = new Properties();
456 requestProperties.setProperty( "filetype", "metadata" );
457 boolean metadataNeedsUpdating = false;
458 long originalTimestamp = getLastModified( localFile );
459
460 List<ProxyConnector> connectors = new ArrayList<>( getProxyConnectors( repository ) );
461 for ( ProxyConnector connector : connectors )
462 {
463 if ( connector.isDisabled() )
464 {
465 continue;
466 }
467
468 RemoteRepositoryContent targetRepository = connector.getTargetRepository();
469
470 File localRepoFile = toLocalRepoFile( repository, targetRepository, logicalPath );
471 long originalMetadataTimestamp = getLastModified( localRepoFile );
472
473 try
474 {
475 transferFile( connector, targetRepository, logicalPath, repository, localRepoFile, requestProperties,
476 true );
477
478 if ( hasBeenUpdated( localRepoFile, originalMetadataTimestamp ) )
479 {
480 metadataNeedsUpdating = true;
481 }
482 }
483 catch ( NotFoundException e )
484 {
485
486 log.debug( "Metadata {} not found on remote repository '{}'.", logicalPath,
487 targetRepository.getRepository().getId(), e );
488
489 }
490 catch ( NotModifiedException e )
491 {
492
493 log.debug( "Metadata {} not updated on remote repository '{}'.", logicalPath,
494 targetRepository.getRepository().getId(), e );
495
496 }
497 catch ( ProxyException | RepositoryAdminException e )
498 {
499 log.warn(
500 "Transfer error from repository {} for versioned Metadata {}, continuing to next repository. Error message: {}",
501 targetRepository.getRepository().getId(), logicalPath, e.getMessage() );
502 log.debug( "Full stack trace", e );
503 }
504 }
505
506 if ( hasBeenUpdated( localFile, originalTimestamp ) )
507 {
508 metadataNeedsUpdating = true;
509 }
510
511 if ( metadataNeedsUpdating || !localFile.exists() )
512 {
513 try
514 {
515 metadataTools.updateMetadata( repository, logicalPath );
516 }
517 catch ( RepositoryMetadataException e )
518 {
519 log.warn( "Unable to update metadata {}:{}", localFile.getAbsolutePath(), e.getMessage(), e );
520 }
521
522 }
523
524 if ( fileExists( localFile ) )
525 {
526 return new ProxyFetchResult( localFile, metadataNeedsUpdating );
527 }
528
529 return new ProxyFetchResult( null, false );
530 }
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547 protected void transferResources( ProxyConnector connector, RemoteRepositoryContent remoteRepository, File tmpMd5,
548 File tmpSha1, File tmpResource, String url, String remotePath, File resource,
549 File workingDirectory, ManagedRepositoryContent repository )
550 throws ProxyException, NotModifiedException, RepositoryAdminException
551 {
552 Wagon wagon = null;
553 try
554 {
555 RepositoryURL repoUrl = remoteRepository.getURL();
556 String protocol = repoUrl.getProtocol();
557 NetworkProxy networkProxy = null;
558 if ( StringUtils.isNotBlank( connector.getProxyId() ) )
559 {
560 networkProxy = networkProxyAdmin.getNetworkProxy( connector.getProxyId() );
561 }
562 WagonFactoryRequest wagonFactoryRequest = new WagonFactoryRequest( "wagon#" + protocol,
563 remoteRepository.getRepository().getExtraHeaders() ).networkProxy(
564 networkProxy );
565 wagon = wagonFactory.getWagon( wagonFactoryRequest );
566 if ( wagon == null )
567 {
568 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
569 }
570
571 if ( wagon == null )
572 {
573 throw new ProxyException( "Unsupported target repository protocol: " + protocol );
574 }
575
576 boolean connected = connectToRepository( connector, wagon, remoteRepository );
577 if ( connected )
578 {
579 transferArtifact( wagon, remoteRepository, remotePath, repository, resource, workingDirectory,
580 tmpResource );
581
582
583
584
585 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory, ".sha1",
586 tmpSha1 );
587 transferChecksum( wagon, remoteRepository, remotePath, repository, resource, workingDirectory, ".md5",
588 tmpMd5 );
589 }
590 }
591 catch ( NotFoundException e )
592 {
593 urlFailureCache.cacheFailure( url );
594 throw e;
595 }
596 catch ( NotModifiedException e )
597 {
598
599 throw e;
600 }
601 catch ( ProxyException e )
602 {
603 urlFailureCache.cacheFailure( url );
604 throw e;
605 }
606 catch ( WagonFactoryException e )
607 {
608 throw new ProxyException( e.getMessage(), e );
609 }
610 finally
611 {
612 if ( wagon != null )
613 {
614 try
615 {
616 wagon.disconnect();
617 }
618 catch ( ConnectionException e )
619 {
620 log.warn( "Unable to disconnect wagon.", e );
621 }
622 }
623 }
624 }
625
626 private void transferArtifact( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
627 ManagedRepositoryContent repository, File resource, File tmpDirectory,
628 File destFile )
629 throws ProxyException
630 {
631 transferSimpleFile( wagon, remoteRepository, remotePath, repository, resource, destFile );
632 }
633
634 private long getLastModified( File file )
635 {
636 if ( !file.exists() || !file.isFile() )
637 {
638 return 0;
639 }
640
641 return file.lastModified();
642 }
643
644 private boolean hasBeenUpdated( File file, long originalLastModified )
645 {
646 if ( !file.exists() || !file.isFile() )
647 {
648 return false;
649 }
650
651 long currentLastModified = getLastModified( file );
652 return ( currentLastModified > originalLastModified );
653 }
654
655 private File toLocalRepoFile( ManagedRepositoryContent repository, RemoteRepositoryContent targetRepository,
656 String targetPath )
657 {
658 String repoPath = metadataTools.getRepositorySpecificName( targetRepository, targetPath );
659 return new File( repository.getRepoRoot(), repoPath );
660 }
661
662
663
664
665 @Override
666 public boolean hasProxies( ManagedRepositoryContent repository )
667 {
668 synchronized ( this.proxyConnectorMap )
669 {
670 return this.proxyConnectorMap.containsKey( repository.getId() );
671 }
672 }
673
674 private File toLocalFile( ManagedRepositoryContent repository, ArtifactReference artifact )
675 {
676 return repository.toFile( artifact );
677 }
678
679
680
681
682
683
684
685 private boolean fileExists( File file )
686 {
687 if ( file == null )
688 {
689 return false;
690 }
691
692 if ( !file.exists() )
693 {
694 return false;
695 }
696
697 return file.isFile();
698 }
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716 private File transferFile( ProxyConnector connector, RemoteRepositoryContent remoteRepository, String remotePath,
717 ManagedRepositoryContent repository, File resource, Properties requestProperties,
718 boolean executeConsumers )
719 throws ProxyException, NotModifiedException, RepositoryAdminException
720 {
721 String url = remoteRepository.getURL().getUrl();
722 if ( !url.endsWith( "/" ) )
723 {
724 url = url + "/";
725 }
726 url = url + remotePath;
727 requestProperties.setProperty( "url", url );
728
729
730 if ( CollectionUtils.isNotEmpty( connector.getWhitelist() ) )
731 {
732
733 if ( !matchesPattern( remotePath, connector.getWhitelist() ) )
734 {
735 log.debug( "Path [{}] is not part of defined whitelist (skipping transfer from repository [{}]).",
736 remotePath, remoteRepository.getRepository().getName() );
737 return null;
738 }
739 }
740
741
742 if ( matchesPattern( remotePath, connector.getBlacklist() ) )
743 {
744 log.debug( "Path [{}] is part of blacklist (skipping transfer from repository [{}]).", remotePath,
745 remoteRepository.getRepository().getName() );
746 return null;
747 }
748
749
750 try
751 {
752 validatePolicies( this.preDownloadPolicies, connector.getPolicies(), requestProperties, resource );
753 }
754 catch ( PolicyViolationException e )
755 {
756 String emsg = "Transfer not attempted on " + url + " : " + e.getMessage();
757 if ( fileExists( resource ) )
758 {
759 log.debug( "{} : using already present local file.", emsg );
760 return resource;
761 }
762
763 log.debug( emsg );
764 return null;
765 }
766
767 File workingDirectory = createWorkingDirectory( repository );
768 File tmpResource = new File( workingDirectory, resource.getName() );
769 File tmpMd5 = new File( workingDirectory, resource.getName() + ".md5" );
770 File tmpSha1 = new File( workingDirectory, resource.getName() + ".sha1" );
771
772 try
773 {
774
775 transferResources( connector, remoteRepository, tmpMd5, tmpSha1, tmpResource, url, remotePath, resource,
776 workingDirectory, repository );
777
778
779 try
780 {
781 validatePolicies( this.postDownloadPolicies, connector.getPolicies(), requestProperties, tmpResource );
782 }
783 catch ( PolicyViolationException e )
784 {
785 log.warn( "Transfer invalidated from {} : {}", url, e.getMessage() );
786 executeConsumers = false;
787 if ( !fileExists( tmpResource ) )
788 {
789 resource = null;
790 }
791 }
792
793 if ( resource != null )
794 {
795 synchronized ( resource.getAbsolutePath().intern() )
796 {
797 File directory = resource.getParentFile();
798 moveFileIfExists( tmpMd5, directory );
799 moveFileIfExists( tmpSha1, directory );
800 moveFileIfExists( tmpResource, directory );
801 }
802 }
803 }
804 finally
805 {
806 FileUtils.deleteQuietly( workingDirectory );
807 }
808
809 if ( executeConsumers )
810 {
811
812
813 queueRepositoryTask( connector.getSourceRepository().getRepository().getId(), resource );
814 }
815
816 return resource;
817 }
818
819 private void queueRepositoryTask( String repositoryId, File localFile )
820 {
821 RepositoryTask task = new RepositoryTask();
822 task.setRepositoryId( repositoryId );
823 task.setResourceFile( localFile );
824 task.setUpdateRelatedArtifacts( true );
825 task.setScanAll( true );
826
827 try
828 {
829 scheduler.queueTask( task );
830 }
831 catch ( TaskQueueException e )
832 {
833 log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
834 + "']." );
835 }
836 }
837
838
839
840
841
842
843
844 private void moveFileIfExists( File fileToMove, File directory )
845 throws ProxyException
846 {
847 if ( fileToMove != null && fileToMove.exists() )
848 {
849 File newLocation = new File( directory, fileToMove.getName() );
850 moveTempToTarget( fileToMove, newLocation );
851 }
852 }
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868 private void transferChecksum( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
869 ManagedRepositoryContent repository, File resource, File tmpDirectory, String ext,
870 File destFile )
871 throws ProxyException
872 {
873 String url = remoteRepository.getURL().getUrl() + remotePath + ext;
874
875
876 if ( urlFailureCache.hasFailedBefore( url ) )
877 {
878 return;
879 }
880
881 try
882 {
883 transferSimpleFile( wagon, remoteRepository, remotePath + ext, repository, resource, destFile );
884 log.debug( "Checksum {} Downloaded: {} to move to {}", url, destFile, resource );
885 }
886 catch ( NotFoundException e )
887 {
888 urlFailureCache.cacheFailure( url );
889 log.debug( "Transfer failed, checksum not found: {}", url );
890
891 }
892 catch ( NotModifiedException e )
893 {
894 log.debug( "Transfer skipped, checksum not modified: {}", url );
895
896 }
897 catch ( ProxyException e )
898 {
899 urlFailureCache.cacheFailure( url );
900 log.warn( "Transfer failed on checksum: {} : {}", url, e.getMessage(), e );
901
902 throw e;
903 }
904 }
905
906
907
908
909
910
911
912
913
914
915
916 private void transferSimpleFile( Wagon wagon, RemoteRepositoryContent remoteRepository, String remotePath,
917 ManagedRepositoryContent repository, File origFile, File destFile )
918 throws ProxyException
919 {
920 assert ( remotePath != null );
921
922
923 try
924 {
925 boolean success = false;
926
927 if ( !origFile.exists() )
928 {
929 log.debug( "Retrieving {} from {}", remotePath, remoteRepository.getRepository().getName() );
930 wagon.get( addParameters( remotePath, remoteRepository.getRepository() ), destFile );
931 success = true;
932
933
934 log.debug( "Downloaded successfully." );
935 }
936 else
937 {
938 log.debug( "Retrieving {} from {} if updated", remotePath, remoteRepository.getRepository().getName() );
939 success = wagon.getIfNewer( addParameters( remotePath, remoteRepository.getRepository() ), destFile,
940 origFile.lastModified() );
941 if ( !success )
942 {
943 throw new NotModifiedException(
944 "Not downloaded, as local file is newer than remote side: " + origFile.getAbsolutePath() );
945 }
946
947 if ( destFile.exists() )
948 {
949 log.debug( "Downloaded successfully." );
950 }
951 }
952 }
953 catch ( ResourceDoesNotExistException e )
954 {
955 throw new NotFoundException(
956 "Resource [" + remoteRepository.getURL() + "/" + remotePath + "] does not exist: " + e.getMessage(),
957 e );
958 }
959 catch ( WagonException e )
960 {
961
962
963 String msg =
964 "Download failure on resource [" + remoteRepository.getURL() + "/" + remotePath + "]:" + e.getMessage();
965 if ( e.getCause() != null )
966 {
967 msg += " (cause: " + e.getCause() + ")";
968 }
969 throw new ProxyException( msg, e );
970 }
971 }
972
973
974
975
976
977
978
979
980
981
982
983
984 private void validatePolicies( Map<String, ? extends DownloadPolicy> policies, Map<String, String> settings,
985 Properties request, File localFile )
986 throws PolicyViolationException
987 {
988 for ( Entry<String, ? extends DownloadPolicy> entry : policies.entrySet() )
989 {
990
991
992 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
993 DownloadPolicy policy = entry.getValue();
994 String defaultSetting = policy.getDefaultOption();
995
996 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
997
998 log.debug( "Applying [{}] policy with [{}]", key, setting );
999 try
1000 {
1001 policy.applyPolicy( setting, request, localFile );
1002 }
1003 catch ( PolicyConfigurationException e )
1004 {
1005 log.error( e.getMessage(), e );
1006 }
1007 }
1008 }
1009
1010 private void validatePolicies( Map<String, DownloadErrorPolicy> policies, Map<String, String> settings,
1011 Properties request, ArtifactReference artifact, RemoteRepositoryContent content,
1012 File localFile, Exception exception, Map<String, Exception> previousExceptions )
1013 throws ProxyDownloadException
1014 {
1015 boolean process = true;
1016 for ( Entry<String, ? extends DownloadErrorPolicy> entry : policies.entrySet() )
1017 {
1018
1019
1020
1021 String key = StringUtils.substringAfterLast( entry.getKey(), "#" );
1022 DownloadErrorPolicy policy = entry.getValue();
1023 String defaultSetting = policy.getDefaultOption();
1024 String setting = StringUtils.defaultString( settings.get( key ), defaultSetting );
1025
1026 log.debug( "Applying [{}] policy with [{}]", key, setting );
1027 try
1028 {
1029
1030 process = policy.applyPolicy( setting, request, localFile, exception, previousExceptions );
1031 if ( !process )
1032 {
1033 break;
1034 }
1035 }
1036 catch ( PolicyConfigurationException e )
1037 {
1038 log.error( e.getMessage(), e );
1039 }
1040 }
1041
1042 if ( process )
1043 {
1044
1045 if ( !previousExceptions.containsKey( content.getId() ) )
1046 {
1047 throw new ProxyDownloadException(
1048 "An error occurred in downloading from the remote repository, and the policy is to fail immediately",
1049 content.getId(), exception );
1050 }
1051 }
1052 else
1053 {
1054
1055 previousExceptions.remove( content.getId() );
1056 }
1057
1058 log.warn(
1059 "Transfer error from repository {} for artifact {} , continuing to next repository. Error message: {}",
1060 content.getRepository().getId(), Keys.toKey( artifact ), exception.getMessage() );
1061 log.debug( "Full stack trace", exception );
1062 }
1063
1064
1065
1066
1067
1068
1069
1070 private File createWorkingDirectory( ManagedRepositoryContent repository )
1071 {
1072 try
1073 {
1074 return Files.createTempDirectory( "temp" ).toFile();
1075 }
1076 catch ( IOException e )
1077 {
1078 throw new RuntimeException( e.getMessage(), e );
1079 }
1080
1081 }
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091 private void moveTempToTarget( File temp, File target )
1092 throws ProxyException
1093 {
1094
1095 Lock lock;
1096 try
1097 {
1098 lock = fileLockManager.writeFileLock( target );
1099 if ( lock.getFile().exists() && !lock.getFile().delete() )
1100 {
1101 throw new ProxyException( "Unable to overwrite existing target file: " + target.getAbsolutePath() );
1102 }
1103
1104 lock.getFile().getParentFile().mkdirs();
1105
1106 if ( !temp.renameTo( lock.getFile() ) )
1107 {
1108 log.warn( "Unable to rename tmp file to its final name... resorting to copy command." );
1109
1110 try
1111 {
1112 FileUtils.copyFile( temp, lock.getFile() );
1113 }
1114 catch ( IOException e )
1115 {
1116 if ( lock.getFile().exists() )
1117 {
1118 log.debug( "Tried to copy file {} to {} but file with this name already exists.",
1119 temp.getName(), lock.getFile().getAbsolutePath() );
1120 }
1121 else
1122 {
1123 throw new ProxyException(
1124 "Cannot copy tmp file " + temp.getAbsolutePath() + " to its final location", e );
1125 }
1126 }
1127 finally
1128 {
1129 FileUtils.deleteQuietly( temp );
1130 }
1131 }
1132 }
1133 catch ( FileLockException | FileLockTimeoutException e )
1134 {
1135 throw new ProxyException( e.getMessage(), e );
1136 }
1137 }
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147 private boolean connectToRepository( ProxyConnector connector, Wagon wagon,
1148 RemoteRepositoryContent remoteRepository )
1149 {
1150 boolean connected = false;
1151
1152 final ProxyInfo networkProxy =
1153 connector.getProxyId() == null ? null : this.networkProxyMap.get( connector.getProxyId() );
1154
1155 if ( log.isDebugEnabled() )
1156 {
1157 if ( networkProxy != null )
1158 {
1159
1160 String msg = "Using network proxy " + networkProxy.getHost() + ":" + networkProxy.getPort()
1161 + " to connect to remote repository " + remoteRepository.getURL();
1162 if ( networkProxy.getNonProxyHosts() != null )
1163 {
1164 msg += "; excluding hosts: " + networkProxy.getNonProxyHosts();
1165 }
1166 if ( StringUtils.isNotBlank( networkProxy.getUserName() ) )
1167 {
1168 msg += "; as user: " + networkProxy.getUserName();
1169 }
1170 log.debug( msg );
1171 }
1172 }
1173
1174 AuthenticationInfo authInfo = null;
1175 String username = remoteRepository.getRepository().getUserName();
1176 String password = remoteRepository.getRepository().getPassword();
1177
1178 if ( StringUtils.isNotBlank( username ) && StringUtils.isNotBlank( password ) )
1179 {
1180 log.debug( "Using username {} to connect to remote repository {}", username, remoteRepository.getURL() );
1181 authInfo = new AuthenticationInfo();
1182 authInfo.setUserName( username );
1183 authInfo.setPassword( password );
1184 }
1185
1186
1187 long timeoutInMilliseconds = TimeUnit.MILLISECONDS.convert( remoteRepository.getRepository().getTimeout(),
1188 TimeUnit.SECONDS );
1189
1190
1191
1192 wagon.setReadTimeout( (int) timeoutInMilliseconds );
1193 wagon.setTimeout( (int) timeoutInMilliseconds );
1194
1195 try
1196 {
1197 Repository wagonRepository =
1198 new Repository( remoteRepository.getId(), remoteRepository.getURL().toString() );
1199 wagon.connect( wagonRepository, authInfo, networkProxy );
1200 connected = true;
1201 }
1202 catch ( ConnectionException | AuthenticationException e )
1203 {
1204 log.warn( "Could not connect to {}: {}", remoteRepository.getRepository().getName(), e.getMessage() );
1205 connected = false;
1206 }
1207
1208 return connected;
1209 }
1210
1211
1212
1213
1214
1215
1216
1217
1218 private boolean matchesPattern( String path, List<String> patterns )
1219 {
1220 if ( CollectionUtils.isEmpty( patterns ) )
1221 {
1222 return false;
1223 }
1224
1225 if ( !path.startsWith( "/" ) )
1226 {
1227 path = "/" + path;
1228 }
1229
1230 for ( String pattern : patterns )
1231 {
1232 if ( !pattern.startsWith( "/" ) )
1233 {
1234 pattern = "/" + pattern;
1235 }
1236
1237 if ( SelectorUtils.matchPath( pattern, path, false ) )
1238 {
1239 return true;
1240 }
1241 }
1242
1243 return false;
1244 }
1245
1246
1247
1248
1249 @Override
1250 public List<ProxyConnector> getProxyConnectors( ManagedRepositoryContent repository )
1251 {
1252
1253 if ( !this.proxyConnectorMap.containsKey( repository.getId() ) )
1254 {
1255 return Collections.emptyList();
1256 }
1257 List<ProxyConnector> ret = new ArrayList<>( this.proxyConnectorMap.get( repository.getId() ) );
1258
1259 Collections.sort( ret, ProxyConnectorOrderComparator.getInstance() );
1260 return ret;
1261
1262 }
1263
1264 @Override
1265 public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1266 {
1267 if ( ConfigurationNames.isNetworkProxy( propertyName )
1268 || ConfigurationNames.isManagedRepositories( propertyName )
1269 || ConfigurationNames.isRemoteRepositories( propertyName )
1270 || ConfigurationNames.isProxyConnector( propertyName ) )
1271 {
1272 initConnectorsAndNetworkProxies();
1273 }
1274 }
1275
1276 protected String addParameters( String path, RemoteRepository remoteRepository )
1277 {
1278 if ( remoteRepository.getExtraParameters().isEmpty() )
1279 {
1280 return path;
1281 }
1282
1283 boolean question = false;
1284
1285 StringBuilder res = new StringBuilder( path == null ? "" : path );
1286
1287 for ( Entry<String, String> entry : remoteRepository.getExtraParameters().entrySet() )
1288 {
1289 if ( !question )
1290 {
1291 res.append( '?' ).append( entry.getKey() ).append( '=' ).append( entry.getValue() );
1292 }
1293 }
1294
1295 return res.toString();
1296 }
1297
1298
1299 @Override
1300 public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
1301 {
1302
1303 }
1304
1305 public ArchivaConfiguration getArchivaConfiguration()
1306 {
1307 return archivaConfiguration;
1308 }
1309
1310 public void setArchivaConfiguration( ArchivaConfiguration archivaConfiguration )
1311 {
1312 this.archivaConfiguration = archivaConfiguration;
1313 }
1314
1315 public RepositoryContentFactory getRepositoryFactory()
1316 {
1317 return repositoryFactory;
1318 }
1319
1320 public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
1321 {
1322 this.repositoryFactory = repositoryFactory;
1323 }
1324
1325 public MetadataTools getMetadataTools()
1326 {
1327 return metadataTools;
1328 }
1329
1330 public void setMetadataTools( MetadataTools metadataTools )
1331 {
1332 this.metadataTools = metadataTools;
1333 }
1334
1335 public UrlFailureCache getUrlFailureCache()
1336 {
1337 return urlFailureCache;
1338 }
1339
1340 public void setUrlFailureCache( UrlFailureCache urlFailureCache )
1341 {
1342 this.urlFailureCache = urlFailureCache;
1343 }
1344
1345 public WagonFactory getWagonFactory()
1346 {
1347 return wagonFactory;
1348 }
1349
1350 public void setWagonFactory( WagonFactory wagonFactory )
1351 {
1352 this.wagonFactory = wagonFactory;
1353 }
1354
1355 public Map<String, PreDownloadPolicy> getPreDownloadPolicies()
1356 {
1357 return preDownloadPolicies;
1358 }
1359
1360 public void setPreDownloadPolicies( Map<String, PreDownloadPolicy> preDownloadPolicies )
1361 {
1362 this.preDownloadPolicies = preDownloadPolicies;
1363 }
1364
1365 public Map<String, PostDownloadPolicy> getPostDownloadPolicies()
1366 {
1367 return postDownloadPolicies;
1368 }
1369
1370 public void setPostDownloadPolicies( Map<String, PostDownloadPolicy> postDownloadPolicies )
1371 {
1372 this.postDownloadPolicies = postDownloadPolicies;
1373 }
1374
1375 public Map<String, DownloadErrorPolicy> getDownloadErrorPolicies()
1376 {
1377 return downloadErrorPolicies;
1378 }
1379
1380 public void setDownloadErrorPolicies( Map<String, DownloadErrorPolicy> downloadErrorPolicies )
1381 {
1382 this.downloadErrorPolicies = downloadErrorPolicies;
1383 }
1384 }