You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DefaultRepositoryProxyHandler.java 38KB

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