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

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