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.

MetadataTools.java 35KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982
  1. package org.apache.archiva.repository.metadata.base;
  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.ChecksummedFile;
  22. import org.apache.archiva.common.utils.PathUtil;
  23. import org.apache.archiva.common.utils.VersionComparator;
  24. import org.apache.archiva.common.utils.VersionUtil;
  25. import org.apache.archiva.configuration.ArchivaConfiguration;
  26. import org.apache.archiva.configuration.ConfigurationNames;
  27. import org.apache.archiva.configuration.FileTypes;
  28. import org.apache.archiva.configuration.ProxyConnectorConfiguration;
  29. import org.apache.archiva.maven2.metadata.MavenMetadataReader;
  30. import org.apache.archiva.model.ArchivaRepositoryMetadata;
  31. import org.apache.archiva.model.ArtifactReference;
  32. import org.apache.archiva.model.Plugin;
  33. import org.apache.archiva.model.ProjectReference;
  34. import org.apache.archiva.model.SnapshotVersion;
  35. import org.apache.archiva.model.VersionedReference;
  36. import org.apache.archiva.redback.components.registry.Registry;
  37. import org.apache.archiva.redback.components.registry.RegistryListener;
  38. import org.apache.archiva.repository.ContentNotFoundException;
  39. import org.apache.archiva.repository.LayoutException;
  40. import org.apache.archiva.repository.ManagedRepositoryContent;
  41. import org.apache.archiva.repository.RemoteRepositoryContent;
  42. import org.apache.archiva.repository.metadata.RepositoryMetadataException;
  43. import org.apache.archiva.repository.storage.StorageAsset;
  44. import org.apache.archiva.xml.XMLException;
  45. import org.apache.commons.collections4.CollectionUtils;
  46. import org.apache.commons.lang3.StringUtils;
  47. import org.apache.commons.lang3.math.NumberUtils;
  48. import org.slf4j.Logger;
  49. import org.slf4j.LoggerFactory;
  50. import org.springframework.stereotype.Service;
  51. import javax.annotation.PostConstruct;
  52. import javax.inject.Inject;
  53. import javax.inject.Named;
  54. import java.io.IOException;
  55. import java.nio.file.Files;
  56. import java.nio.file.Path;
  57. import java.nio.file.Paths;
  58. import java.text.ParseException;
  59. import java.text.SimpleDateFormat;
  60. import java.util.*;
  61. import java.util.regex.Matcher;
  62. import java.util.stream.Stream;
  63. /**
  64. * MetadataTools
  65. *
  66. *
  67. */
  68. @Service( "metadataTools#default" )
  69. public class MetadataTools
  70. implements RegistryListener
  71. {
  72. private Logger log = LoggerFactory.getLogger( getClass() );
  73. public static final String MAVEN_METADATA = "maven-metadata.xml";
  74. public static final String MAVEN_ARCHETYPE_CATALOG ="archetype-catalog.xml";
  75. private static final char PATH_SEPARATOR = '/';
  76. private static final char GROUP_SEPARATOR = '.';
  77. /**
  78. *
  79. */
  80. @Inject
  81. @Named( value = "archivaConfiguration#default" )
  82. private ArchivaConfiguration configuration;
  83. /**
  84. *
  85. */
  86. @Inject
  87. @Named( value = "fileTypes" )
  88. private FileTypes filetypes;
  89. private List<ChecksumAlgorithm> algorithms = Arrays.asList(ChecksumAlgorithm.SHA256, ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 );
  90. private List<String> artifactPatterns;
  91. private Map<String, Set<String>> proxies;
  92. private static final char NUMS[] = new char[]{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
  93. private SimpleDateFormat lastUpdatedFormat;
  94. public MetadataTools()
  95. {
  96. lastUpdatedFormat = new SimpleDateFormat( "yyyyMMddHHmmss" );
  97. lastUpdatedFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
  98. }
  99. @Override
  100. public void afterConfigurationChange( Registry registry, String propertyName, Object propertyValue )
  101. {
  102. if ( ConfigurationNames.isProxyConnector( propertyName ) )
  103. {
  104. initConfigVariables();
  105. }
  106. }
  107. @Override
  108. public void beforeConfigurationChange( Registry registry, String propertyName, Object propertyValue )
  109. {
  110. /* nothing to do */
  111. }
  112. /**
  113. * Gather the set of snapshot versions found in a particular versioned reference.
  114. *
  115. * @return the Set of snapshot artifact versions found.
  116. * @throws LayoutException
  117. * @throws ContentNotFoundException
  118. */
  119. public Set<String> gatherSnapshotVersions( ManagedRepositoryContent managedRepository,
  120. VersionedReference reference )
  121. throws LayoutException, IOException, ContentNotFoundException
  122. {
  123. Set<String> foundVersions = managedRepository.getVersions( reference );
  124. // Next gather up the referenced 'latest' versions found in any proxied repositories
  125. // maven-metadata-${proxyId}.xml files that may be present.
  126. // Does this repository have a set of remote proxied repositories?
  127. Set<String> proxiedRepoIds = this.proxies.get( managedRepository.getId() );
  128. if ( CollectionUtils.isNotEmpty( proxiedRepoIds ) )
  129. {
  130. String baseVersion = VersionUtil.getBaseVersion( reference.getVersion() );
  131. baseVersion = baseVersion.substring( 0, baseVersion.indexOf( VersionUtil.SNAPSHOT ) - 1 );
  132. // Add in the proxied repo version ids too.
  133. Iterator<String> it = proxiedRepoIds.iterator();
  134. while ( it.hasNext() )
  135. {
  136. String proxyId = it.next();
  137. ArchivaRepositoryMetadata proxyMetadata = readProxyMetadata( managedRepository, reference, proxyId );
  138. if ( proxyMetadata == null )
  139. {
  140. // There is no proxy metadata, skip it.
  141. continue;
  142. }
  143. // Is there some snapshot info?
  144. SnapshotVersion snapshot = proxyMetadata.getSnapshotVersion();
  145. if ( snapshot != null )
  146. {
  147. String timestamp = snapshot.getTimestamp();
  148. int buildNumber = snapshot.getBuildNumber();
  149. // Only interested in the timestamp + buildnumber.
  150. if ( StringUtils.isNotBlank( timestamp ) && ( buildNumber > 0 ) )
  151. {
  152. foundVersions.add( baseVersion + "-" + timestamp + "-" + buildNumber );
  153. }
  154. }
  155. }
  156. }
  157. return foundVersions;
  158. }
  159. /**
  160. * Take a path to a maven-metadata.xml, and attempt to translate it to a VersionedReference.
  161. *
  162. * @param path
  163. * @return
  164. */
  165. public VersionedReference toVersionedReference( String path )
  166. throws RepositoryMetadataException
  167. {
  168. if ( !path.endsWith( "/" + MAVEN_METADATA ) )
  169. {
  170. throw new RepositoryMetadataException( "Cannot convert to versioned reference, not a metadata file. " );
  171. }
  172. VersionedReference reference = new VersionedReference();
  173. String normalizedPath = StringUtils.replace( path, "\\", "/" );
  174. String pathParts[] = StringUtils.split( normalizedPath, '/' );
  175. int versionOffset = pathParts.length - 2;
  176. int artifactIdOffset = versionOffset - 1;
  177. int groupIdEnd = artifactIdOffset - 1;
  178. reference.setVersion( pathParts[versionOffset] );
  179. if ( !hasNumberAnywhere( reference.getVersion() ) )
  180. {
  181. // Scary check, but without it, all paths are version references;
  182. throw new RepositoryMetadataException(
  183. "Not a versioned reference, as version id on path has no number in it." );
  184. }
  185. reference.setArtifactId( pathParts[artifactIdOffset] );
  186. StringBuilder gid = new StringBuilder();
  187. for ( int i = 0; i <= groupIdEnd; i++ )
  188. {
  189. if ( i > 0 )
  190. {
  191. gid.append( "." );
  192. }
  193. gid.append( pathParts[i] );
  194. }
  195. reference.setGroupId( gid.toString() );
  196. return reference;
  197. }
  198. private boolean hasNumberAnywhere( String version )
  199. {
  200. return StringUtils.indexOfAny( version, NUMS ) != ( -1 );
  201. }
  202. public ProjectReference toProjectReference( String path )
  203. throws RepositoryMetadataException
  204. {
  205. if ( !path.endsWith( "/" + MAVEN_METADATA ) )
  206. {
  207. throw new RepositoryMetadataException( "Cannot convert to versioned reference, not a metadata file. " );
  208. }
  209. ProjectReference reference = new ProjectReference();
  210. String normalizedPath = StringUtils.replace( path, "\\", "/" );
  211. String pathParts[] = StringUtils.split( normalizedPath, '/' );
  212. // Assume last part of the path is the version.
  213. int artifactIdOffset = pathParts.length - 2;
  214. int groupIdEnd = artifactIdOffset - 1;
  215. reference.setArtifactId( pathParts[artifactIdOffset] );
  216. StringBuilder gid = new StringBuilder();
  217. for ( int i = 0; i <= groupIdEnd; i++ )
  218. {
  219. if ( i > 0 )
  220. {
  221. gid.append( "." );
  222. }
  223. gid.append( pathParts[i] );
  224. }
  225. reference.setGroupId( gid.toString() );
  226. return reference;
  227. }
  228. public String toPath( ProjectReference reference )
  229. {
  230. StringBuilder path = new StringBuilder();
  231. path.append( formatAsDirectory( reference.getGroupId() ) ).append( PATH_SEPARATOR );
  232. path.append( reference.getArtifactId() ).append( PATH_SEPARATOR );
  233. path.append( MAVEN_METADATA );
  234. return path.toString();
  235. }
  236. public String toPath( VersionedReference reference )
  237. {
  238. StringBuilder path = new StringBuilder();
  239. path.append( formatAsDirectory( reference.getGroupId() ) ).append( PATH_SEPARATOR );
  240. path.append( reference.getArtifactId() ).append( PATH_SEPARATOR );
  241. if ( reference.getVersion() != null )
  242. {
  243. // add the version only if it is present
  244. path.append( VersionUtil.getBaseVersion( reference.getVersion() ) ).append( PATH_SEPARATOR );
  245. }
  246. path.append( MAVEN_METADATA );
  247. return path.toString();
  248. }
  249. private String formatAsDirectory( String directory )
  250. {
  251. return directory.replace( GROUP_SEPARATOR, PATH_SEPARATOR );
  252. }
  253. /**
  254. * Adjusts a path for a metadata.xml file to its repository specific path.
  255. *
  256. * @param repository the repository to base new path off of.
  257. * @param path the path to the metadata.xml file to adjust the name of.
  258. * @return the newly adjusted path reference to the repository specific metadata path.
  259. */
  260. public String getRepositorySpecificName( RemoteRepositoryContent repository, String path )
  261. {
  262. return getRepositorySpecificName( repository.getId(), path );
  263. }
  264. /**
  265. * Adjusts a path for a metadata.xml file to its repository specific path.
  266. *
  267. * @param proxyId the repository id to base new path off of.
  268. * @param path the path to the metadata.xml file to adjust the name of.
  269. * @return the newly adjusted path reference to the repository specific metadata path.
  270. */
  271. public String getRepositorySpecificName( String proxyId, String path )
  272. {
  273. StringBuilder ret = new StringBuilder();
  274. int idx = path.lastIndexOf( '/' );
  275. if ( idx > 0 )
  276. {
  277. ret.append( path.substring( 0, idx + 1 ) );
  278. }
  279. // TODO: need to filter out 'bad' characters from the proxy id.
  280. ret.append( "maven-metadata-" ).append( proxyId ).append( ".xml" );
  281. return ret.toString();
  282. }
  283. @PostConstruct
  284. public void initialize()
  285. {
  286. assert(configuration != null);
  287. this.artifactPatterns = new ArrayList<>();
  288. this.proxies = new HashMap<>();
  289. initConfigVariables();
  290. configuration.addChangeListener( this );
  291. }
  292. public ArchivaRepositoryMetadata readProxyMetadata( ManagedRepositoryContent managedRepository,
  293. ProjectReference reference, String proxyId )
  294. {
  295. String metadataPath = getRepositorySpecificName( proxyId, toPath( reference ) );
  296. StorageAsset metadataFile = managedRepository.getRepository().getAsset( metadataPath );
  297. if ( !metadataFile.exists() || metadataFile.isContainer())
  298. {
  299. // Nothing to do. return null.
  300. return null;
  301. }
  302. try
  303. {
  304. return MavenMetadataReader.read( metadataFile );
  305. }
  306. catch (XMLException | IOException e )
  307. {
  308. // TODO: [monitor] consider a monitor for this event.
  309. // TODO: consider a read-redo on monitor return code?
  310. log.warn( "Unable to read metadata: {}", metadataFile.getPath(), e );
  311. return null;
  312. }
  313. }
  314. public ArchivaRepositoryMetadata readProxyMetadata( ManagedRepositoryContent managedRepository,
  315. String logicalResource, String proxyId )
  316. {
  317. String metadataPath = getRepositorySpecificName( proxyId, logicalResource );
  318. StorageAsset metadataFile = managedRepository.getRepository().getAsset( metadataPath );
  319. if ( !metadataFile.exists() || metadataFile.isContainer())
  320. {
  321. // Nothing to do. return null.
  322. return null;
  323. }
  324. try
  325. {
  326. return MavenMetadataReader.read( metadataFile );
  327. }
  328. catch (XMLException | IOException e )
  329. {
  330. // TODO: [monitor] consider a monitor for this event.
  331. // TODO: consider a read-redo on monitor return code?
  332. log.warn( "Unable to read metadata: {}", metadataFile.getPath(), e );
  333. return null;
  334. }
  335. }
  336. public ArchivaRepositoryMetadata readProxyMetadata( ManagedRepositoryContent managedRepository,
  337. VersionedReference reference, String proxyId )
  338. {
  339. String metadataPath = getRepositorySpecificName( proxyId, toPath( reference ) );
  340. StorageAsset metadataFile = managedRepository.getRepository().getAsset( metadataPath );
  341. if ( !metadataFile.exists() || metadataFile.isContainer())
  342. {
  343. // Nothing to do. return null.
  344. return null;
  345. }
  346. try
  347. {
  348. return MavenMetadataReader.read( metadataFile );
  349. }
  350. catch (XMLException | IOException e )
  351. {
  352. // TODO: [monitor] consider a monitor for this event.
  353. // TODO: consider a read-redo on monitor return code?
  354. log.warn( "Unable to read metadata: {}", metadataFile.getPath(), e );
  355. return null;
  356. }
  357. }
  358. public void updateMetadata( ManagedRepositoryContent managedRepository, String logicalResource )
  359. throws RepositoryMetadataException
  360. {
  361. final StorageAsset metadataFile = managedRepository.getRepository().getAsset( logicalResource );
  362. ArchivaRepositoryMetadata metadata = null;
  363. //Gather and merge all metadata available
  364. List<ArchivaRepositoryMetadata> metadatas =
  365. getMetadatasForManagedRepository( managedRepository, logicalResource );
  366. for ( ArchivaRepositoryMetadata proxiedMetadata : metadatas )
  367. {
  368. if ( metadata == null )
  369. {
  370. metadata = proxiedMetadata;
  371. continue;
  372. }
  373. metadata = RepositoryMetadataMerge.merge( metadata, proxiedMetadata );
  374. }
  375. if ( metadata == null )
  376. {
  377. log.debug( "No metadata to update for {}", logicalResource );
  378. return;
  379. }
  380. Set<String> availableVersions = new HashSet<String>();
  381. List<String> metadataAvailableVersions = metadata.getAvailableVersions();
  382. if ( metadataAvailableVersions != null )
  383. {
  384. availableVersions.addAll( metadataAvailableVersions );
  385. }
  386. availableVersions = findPossibleVersions( availableVersions, metadataFile.getParent() );
  387. if ( availableVersions.size() > 0 )
  388. {
  389. updateMetadataVersions( availableVersions, metadata );
  390. }
  391. RepositoryMetadataWriter.write( metadata, metadataFile );
  392. ChecksummedFile checksum = new ChecksummedFile( metadataFile.getFilePath() );
  393. checksum.fixChecksums( algorithms );
  394. }
  395. /**
  396. * Skims the parent directory of a metadata in vain hope of finding
  397. * subdirectories that contain poms.
  398. *
  399. * @param metadataParentDirectory
  400. * @return origional set plus newly found versions
  401. */
  402. private Set<String> findPossibleVersions( Set<String> versions, StorageAsset metadataParentDirectory )
  403. {
  404. Set<String> result = new HashSet<String>( versions );
  405. metadataParentDirectory.list().stream().filter(asset ->
  406. asset.isContainer()).filter(asset -> {
  407. return asset.list().stream().anyMatch(f -> !f.isContainer() && f.getName().endsWith(".pom"));
  408. }
  409. ).forEach( p -> result.add(p.getName()));
  410. return result;
  411. }
  412. private List<ArchivaRepositoryMetadata> getMetadatasForManagedRepository(
  413. ManagedRepositoryContent managedRepository, String logicalResource )
  414. {
  415. List<ArchivaRepositoryMetadata> metadatas = new ArrayList<>();
  416. StorageAsset file = managedRepository.getRepository().getAsset( logicalResource );
  417. if ( file.exists() )
  418. {
  419. try
  420. {
  421. ArchivaRepositoryMetadata existingMetadata = MavenMetadataReader.read( file );
  422. if ( existingMetadata != null )
  423. {
  424. metadatas.add( existingMetadata );
  425. }
  426. }
  427. catch (XMLException | IOException e )
  428. {
  429. log.debug( "Could not read metadata at {}. Metadata will be removed.", file.getPath() );
  430. try {
  431. file.getStorage().removeAsset(file);
  432. } catch (IOException ex) {
  433. log.error("Could not remove asset {}", file.getPath());
  434. }
  435. }
  436. }
  437. Set<String> proxyIds = proxies.get( managedRepository.getId() );
  438. if ( proxyIds != null )
  439. {
  440. for ( String proxyId : proxyIds )
  441. {
  442. ArchivaRepositoryMetadata proxyMetadata =
  443. readProxyMetadata( managedRepository, logicalResource, proxyId );
  444. if ( proxyMetadata != null )
  445. {
  446. metadatas.add( proxyMetadata );
  447. }
  448. }
  449. }
  450. return metadatas;
  451. }
  452. /**
  453. * Update the metadata to represent the all versions/plugins of
  454. * the provided groupId:artifactId project or group reference,
  455. * based off of information present in the repository,
  456. * the maven-metadata.xml files, and the proxy/repository specific
  457. * metadata file contents.
  458. * <p>
  459. * We must treat this as a group or a project metadata file as there is no way to know in advance
  460. *
  461. * @param managedRepository the managed repository where the metadata is kept.
  462. * @param reference the reference to update.
  463. * @throws LayoutException
  464. * @throws RepositoryMetadataException
  465. * @throws IOException
  466. * @throws ContentNotFoundException
  467. * @deprecated
  468. */
  469. public void updateMetadata( ManagedRepositoryContent managedRepository, ProjectReference reference )
  470. throws LayoutException, RepositoryMetadataException, IOException, ContentNotFoundException
  471. {
  472. StorageAsset metadataFile = managedRepository.getRepository().getAsset( toPath( reference ) );
  473. long lastUpdated = getExistingLastUpdated( metadataFile );
  474. ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
  475. metadata.setGroupId( reference.getGroupId() );
  476. metadata.setArtifactId( reference.getArtifactId() );
  477. // Gather up all versions found in the managed repository.
  478. Set<String> allVersions = managedRepository.getVersions( reference );
  479. // Gather up all plugins found in the managed repository.
  480. // TODO: do we know this information instead?
  481. // Set<Plugin> allPlugins = managedRepository.getPlugins( reference );
  482. Set<Plugin> allPlugins;
  483. if ( metadataFile.exists())
  484. {
  485. try
  486. {
  487. allPlugins = new LinkedHashSet<Plugin>( MavenMetadataReader.read( metadataFile ).getPlugins() );
  488. }
  489. catch ( XMLException e )
  490. {
  491. throw new RepositoryMetadataException( e.getMessage(), e );
  492. }
  493. }
  494. else
  495. {
  496. allPlugins = new LinkedHashSet<Plugin>();
  497. }
  498. // Does this repository have a set of remote proxied repositories?
  499. Set<String> proxiedRepoIds = this.proxies.get( managedRepository.getId() );
  500. if ( CollectionUtils.isNotEmpty( proxiedRepoIds ) )
  501. {
  502. // Add in the proxied repo version ids too.
  503. Iterator<String> it = proxiedRepoIds.iterator();
  504. while ( it.hasNext() )
  505. {
  506. String proxyId = it.next();
  507. ArchivaRepositoryMetadata proxyMetadata = readProxyMetadata( managedRepository, reference, proxyId );
  508. if ( proxyMetadata != null )
  509. {
  510. allVersions.addAll( proxyMetadata.getAvailableVersions() );
  511. allPlugins.addAll( proxyMetadata.getPlugins() );
  512. long proxyLastUpdated = getLastUpdated( proxyMetadata );
  513. lastUpdated = Math.max( lastUpdated, proxyLastUpdated );
  514. }
  515. }
  516. }
  517. if ( !allVersions.isEmpty() )
  518. {
  519. updateMetadataVersions( allVersions, metadata );
  520. }
  521. else
  522. {
  523. // Add the plugins to the metadata model.
  524. metadata.setPlugins( new ArrayList<>( allPlugins ) );
  525. // artifact ID was actually the last part of the group
  526. metadata.setGroupId( metadata.getGroupId() + "." + metadata.getArtifactId() );
  527. metadata.setArtifactId( null );
  528. }
  529. if ( lastUpdated > 0 )
  530. {
  531. metadata.setLastUpdatedTimestamp( toLastUpdatedDate( lastUpdated ) );
  532. }
  533. // Save the metadata model to disk.
  534. RepositoryMetadataWriter.write( metadata, metadataFile );
  535. ChecksummedFile checksum = new ChecksummedFile( metadataFile.getFilePath() );
  536. checksum.fixChecksums( algorithms );
  537. }
  538. private void updateMetadataVersions( Collection<String> allVersions, ArchivaRepositoryMetadata metadata )
  539. {
  540. // Sort the versions
  541. List<String> sortedVersions = new ArrayList<>( allVersions );
  542. Collections.sort( sortedVersions, VersionComparator.getInstance() );
  543. // Split the versions into released and snapshots.
  544. List<String> releasedVersions = new ArrayList<>();
  545. List<String> snapshotVersions = new ArrayList<>();
  546. for ( String version : sortedVersions )
  547. {
  548. if ( VersionUtil.isSnapshot( version ) )
  549. {
  550. snapshotVersions.add( version );
  551. }
  552. else
  553. {
  554. releasedVersions.add( version );
  555. }
  556. }
  557. Collections.sort( releasedVersions, VersionComparator.getInstance() );
  558. Collections.sort( snapshotVersions, VersionComparator.getInstance() );
  559. String latestVersion = sortedVersions.get( sortedVersions.size() - 1 );
  560. String releaseVersion = null;
  561. if ( CollectionUtils.isNotEmpty( releasedVersions ) )
  562. {
  563. releaseVersion = releasedVersions.get( releasedVersions.size() - 1 );
  564. }
  565. // Add the versions to the metadata model.
  566. metadata.setAvailableVersions( sortedVersions );
  567. metadata.setLatestVersion( latestVersion );
  568. metadata.setReleasedVersion( releaseVersion );
  569. }
  570. private Date toLastUpdatedDate( long lastUpdated )
  571. {
  572. Calendar cal = Calendar.getInstance( TimeZone.getTimeZone("UTC") );
  573. cal.setTimeInMillis( lastUpdated );
  574. return cal.getTime();
  575. }
  576. private long toLastUpdatedLong( String timestampString )
  577. {
  578. try
  579. {
  580. Date date = lastUpdatedFormat.parse( timestampString );
  581. Calendar cal = Calendar.getInstance( TimeZone.getTimeZone("UTC"));
  582. cal.setTime( date );
  583. return cal.getTimeInMillis();
  584. }
  585. catch ( ParseException e )
  586. {
  587. return 0;
  588. }
  589. }
  590. private long getLastUpdated( ArchivaRepositoryMetadata metadata )
  591. {
  592. if ( metadata == null )
  593. {
  594. // Doesn't exist.
  595. return 0;
  596. }
  597. try
  598. {
  599. String lastUpdated = metadata.getLastUpdated();
  600. if ( StringUtils.isBlank( lastUpdated ) )
  601. {
  602. // Not set.
  603. return 0;
  604. }
  605. Date lastUpdatedDate = lastUpdatedFormat.parse( lastUpdated );
  606. return lastUpdatedDate.getTime();
  607. }
  608. catch ( ParseException e )
  609. {
  610. // Bad format on the last updated string.
  611. return 0;
  612. }
  613. }
  614. private long getExistingLastUpdated( StorageAsset metadataFile )
  615. {
  616. if ( !metadataFile.exists() )
  617. {
  618. // Doesn't exist.
  619. return 0;
  620. }
  621. try
  622. {
  623. ArchivaRepositoryMetadata metadata = MavenMetadataReader.read( metadataFile );
  624. return getLastUpdated( metadata );
  625. }
  626. catch (XMLException | IOException e )
  627. {
  628. // Error.
  629. return 0;
  630. }
  631. }
  632. /**
  633. * Update the metadata based on the following rules.
  634. * <p>
  635. * 1) If this is a SNAPSHOT reference, then utilize the proxy/repository specific
  636. * metadata files to represent the current / latest SNAPSHOT available.
  637. * 2) If this is a RELEASE reference, and the metadata file does not exist, then
  638. * create the metadata file with contents required of the VersionedReference
  639. *
  640. * @param managedRepository the managed repository where the metadata is kept.
  641. * @param reference the versioned reference to update
  642. * @throws LayoutException
  643. * @throws RepositoryMetadataException
  644. * @throws IOException
  645. * @throws ContentNotFoundException
  646. * @deprecated
  647. */
  648. public void updateMetadata( ManagedRepositoryContent managedRepository, VersionedReference reference )
  649. throws LayoutException, RepositoryMetadataException, IOException, ContentNotFoundException
  650. {
  651. StorageAsset metadataFile = managedRepository.getRepository().getAsset( toPath( reference ) );
  652. long lastUpdated = getExistingLastUpdated( metadataFile );
  653. ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
  654. metadata.setGroupId( reference.getGroupId() );
  655. metadata.setArtifactId( reference.getArtifactId() );
  656. if ( VersionUtil.isSnapshot( reference.getVersion() ) )
  657. {
  658. // Do SNAPSHOT handling.
  659. metadata.setVersion( VersionUtil.getBaseVersion( reference.getVersion() ) );
  660. // Gather up all of the versions found in the reference dir, and any
  661. // proxied maven-metadata.xml files.
  662. Set<String> snapshotVersions = gatherSnapshotVersions( managedRepository, reference );
  663. if ( snapshotVersions.isEmpty() )
  664. {
  665. throw new ContentNotFoundException(
  666. "No snapshot versions found on reference [" + VersionedReference.toKey( reference ) + "]." );
  667. }
  668. // sort the list to determine to aide in determining the Latest version.
  669. List<String> sortedVersions = new ArrayList<>();
  670. sortedVersions.addAll( snapshotVersions );
  671. Collections.sort( sortedVersions, new VersionComparator() );
  672. String latestVersion = sortedVersions.get( sortedVersions.size() - 1 );
  673. if ( VersionUtil.isUniqueSnapshot( latestVersion ) )
  674. {
  675. // The latestVersion will contain the full version string "1.0-alpha-5-20070821.213044-8"
  676. // This needs to be broken down into ${base}-${timestamp}-${build_number}
  677. Matcher m = VersionUtil.UNIQUE_SNAPSHOT_PATTERN.matcher( latestVersion );
  678. if ( m.matches() )
  679. {
  680. metadata.setSnapshotVersion( new SnapshotVersion() );
  681. int buildNumber = NumberUtils.toInt( m.group( 3 ), -1 );
  682. metadata.getSnapshotVersion().setBuildNumber( buildNumber );
  683. Matcher mtimestamp = VersionUtil.TIMESTAMP_PATTERN.matcher( m.group( 2 ) );
  684. if ( mtimestamp.matches() )
  685. {
  686. String tsDate = mtimestamp.group( 1 );
  687. String tsTime = mtimestamp.group( 2 );
  688. long snapshotLastUpdated = toLastUpdatedLong( tsDate + tsTime );
  689. lastUpdated = Math.max( lastUpdated, snapshotLastUpdated );
  690. metadata.getSnapshotVersion().setTimestamp( m.group( 2 ) );
  691. }
  692. }
  693. }
  694. else if ( VersionUtil.isGenericSnapshot( latestVersion ) )
  695. {
  696. // The latestVersion ends with the generic version string.
  697. // Example: 1.0-alpha-5-SNAPSHOT
  698. metadata.setSnapshotVersion( new SnapshotVersion() );
  699. /* Disabled due to decision in [MRM-535].
  700. * Do not set metadata.lastUpdated to file.lastModified.
  701. *
  702. * Should this be the last updated timestamp of the file, or in the case of an
  703. * archive, the most recent timestamp in the archive?
  704. *
  705. ArtifactReference artifact = getFirstArtifact( managedRepository, reference );
  706. if ( artifact == null )
  707. {
  708. throw new IOException( "Not snapshot artifact found to reference in " + reference );
  709. }
  710. File artifactFile = managedRepository.toFile( artifact );
  711. if ( artifactFile.exists() )
  712. {
  713. Date lastModified = new Date( artifactFile.lastModified() );
  714. metadata.setLastUpdatedTimestamp( lastModified );
  715. }
  716. */
  717. }
  718. else
  719. {
  720. throw new RepositoryMetadataException(
  721. "Unable to process snapshot version <" + latestVersion + "> reference <" + reference + ">" );
  722. }
  723. }
  724. else
  725. {
  726. // Do RELEASE handling.
  727. metadata.setVersion( reference.getVersion() );
  728. }
  729. // Set last updated
  730. if ( lastUpdated > 0 )
  731. {
  732. metadata.setLastUpdatedTimestamp( toLastUpdatedDate( lastUpdated ) );
  733. }
  734. // Save the metadata model to disk.
  735. RepositoryMetadataWriter.write( metadata, metadataFile );
  736. ChecksummedFile checksum = new ChecksummedFile( metadataFile.getFilePath() );
  737. checksum.fixChecksums( algorithms );
  738. }
  739. private void initConfigVariables()
  740. {
  741. assert(this.artifactPatterns!=null);
  742. assert(proxies!=null);
  743. synchronized ( this.artifactPatterns )
  744. {
  745. this.artifactPatterns.clear();
  746. this.artifactPatterns.addAll( filetypes.getFileTypePatterns( FileTypes.ARTIFACTS ) );
  747. }
  748. synchronized ( proxies )
  749. {
  750. this.proxies.clear();
  751. List<ProxyConnectorConfiguration> proxyConfigs = configuration.getConfiguration().getProxyConnectors();
  752. for ( ProxyConnectorConfiguration proxyConfig : proxyConfigs )
  753. {
  754. String key = proxyConfig.getSourceRepoId();
  755. Set<String> remoteRepoIds = this.proxies.get( key );
  756. if ( remoteRepoIds == null )
  757. {
  758. remoteRepoIds = new HashSet<String>();
  759. }
  760. remoteRepoIds.add( proxyConfig.getTargetRepoId() );
  761. this.proxies.put( key, remoteRepoIds );
  762. }
  763. }
  764. }
  765. /**
  766. * Get the first Artifact found in the provided VersionedReference location.
  767. *
  768. * @param managedRepository the repository to search within.
  769. * @param reference the reference to the versioned reference to search within
  770. * @return the ArtifactReference to the first artifact located within the versioned reference. or null if
  771. * no artifact was found within the versioned reference.
  772. * @throws IOException if the versioned reference is invalid (example: doesn't exist, or isn't a directory)
  773. * @throws LayoutException
  774. */
  775. public ArtifactReference getFirstArtifact( ManagedRepositoryContent managedRepository,
  776. VersionedReference reference )
  777. throws LayoutException, IOException
  778. {
  779. String path = toPath( reference );
  780. int idx = path.lastIndexOf( '/' );
  781. if ( idx > 0 )
  782. {
  783. path = path.substring( 0, idx );
  784. }
  785. Path repoDir = Paths.get( managedRepository.getRepoRoot(), path );
  786. if ( !Files.exists(repoDir))
  787. {
  788. throw new IOException( "Unable to gather the list of snapshot versions on a non-existant directory: "
  789. + repoDir.toAbsolutePath() );
  790. }
  791. if ( !Files.isDirectory( repoDir ))
  792. {
  793. throw new IOException(
  794. "Unable to gather the list of snapshot versions on a non-directory: " + repoDir.toAbsolutePath() );
  795. }
  796. try(Stream<Path> stream = Files.list(repoDir)) {
  797. String result = stream.filter( Files::isRegularFile ).map( path1 ->
  798. PathUtil.getRelative( managedRepository.getRepoRoot(), path1 )
  799. ).filter( filetypes::matchesArtifactPattern ).findFirst().orElse( null );
  800. if (result!=null) {
  801. return managedRepository.toArtifactReference( result );
  802. }
  803. }
  804. // No artifact was found.
  805. return null;
  806. }
  807. public ArchivaConfiguration getConfiguration()
  808. {
  809. return configuration;
  810. }
  811. public void setConfiguration( ArchivaConfiguration configuration )
  812. {
  813. this.configuration = configuration;
  814. }
  815. public FileTypes getFiletypes()
  816. {
  817. return filetypes;
  818. }
  819. public void setFiletypes( FileTypes filetypes )
  820. {
  821. this.filetypes = filetypes;
  822. }
  823. }