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

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