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.

Maven2RepositoryMerger.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. package org.apache.archiva.stagerepository.merge;
  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.common.filelock.DefaultFileLockManager;
  21. import org.apache.archiva.common.utils.VersionComparator;
  22. import org.apache.archiva.common.utils.VersionUtil;
  23. import org.apache.archiva.configuration.ArchivaConfiguration;
  24. import org.apache.archiva.configuration.Configuration;
  25. import org.apache.archiva.configuration.ManagedRepositoryConfiguration;
  26. import org.apache.archiva.maven2.metadata.MavenMetadataReader;
  27. import org.apache.archiva.metadata.model.ArtifactMetadata;
  28. import org.apache.archiva.metadata.repository.MetadataRepository;
  29. import org.apache.archiva.metadata.repository.MetadataRepositoryException;
  30. import org.apache.archiva.filter.Filter;
  31. import org.apache.archiva.metadata.repository.RepositorySession;
  32. import org.apache.archiva.metadata.repository.RepositorySessionFactory;
  33. import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
  34. import org.apache.archiva.model.ArchivaRepositoryMetadata;
  35. import org.apache.archiva.repository.RepositoryException;
  36. import org.apache.archiva.repository.RepositoryType;
  37. import org.apache.archiva.repository.metadata.RepositoryMetadataException;
  38. import org.apache.archiva.repository.metadata.base.RepositoryMetadataWriter;
  39. import org.apache.archiva.repository.storage.FilesystemAsset;
  40. import org.apache.archiva.repository.storage.FilesystemStorage;
  41. import org.apache.archiva.repository.storage.StorageAsset;
  42. import org.apache.archiva.xml.XMLException;
  43. import org.apache.commons.io.FileUtils;
  44. import org.slf4j.Logger;
  45. import org.slf4j.LoggerFactory;
  46. import org.springframework.stereotype.Service;
  47. import javax.inject.Inject;
  48. import javax.inject.Named;
  49. import java.io.BufferedWriter;
  50. import java.io.IOException;
  51. import java.nio.file.Files;
  52. import java.nio.file.Path;
  53. import java.nio.file.Paths;
  54. import java.text.DateFormat;
  55. import java.text.SimpleDateFormat;
  56. import java.util.*;
  57. /**
  58. *
  59. */
  60. @Service ("repositoryMerger#maven2")
  61. public class Maven2RepositoryMerger
  62. implements RepositoryMerger
  63. {
  64. private Logger log = LoggerFactory.getLogger( getClass() );
  65. private static final Comparator<ArtifactMetadata> META_COMPARATOR = Comparator.comparing(ArtifactMetadata::getNamespace)
  66. .thenComparing(ArtifactMetadata::getProject)
  67. .thenComparing(ArtifactMetadata::getId)
  68. .thenComparing(ArtifactMetadata::getVersion);
  69. /**
  70. *
  71. */
  72. private ArchivaConfiguration configuration;
  73. /**
  74. *
  75. */
  76. private RepositoryPathTranslator pathTranslator;
  77. private static final String METADATA_FILENAME = "maven-metadata.xml";
  78. @Inject
  79. private RepositorySessionFactory repositorySessionFactory;
  80. @Inject
  81. public Maven2RepositoryMerger(
  82. @Named (value = "archivaConfiguration#default") ArchivaConfiguration archivaConfiguration,
  83. @Named (value = "repositoryPathTranslator#maven2") RepositoryPathTranslator repositoryPathTranslator )
  84. {
  85. this.configuration = archivaConfiguration;
  86. this.pathTranslator = repositoryPathTranslator;
  87. }
  88. public void setConfiguration( ArchivaConfiguration configuration )
  89. {
  90. this.configuration = configuration;
  91. }
  92. @Override
  93. public boolean supportsRepository( RepositoryType type )
  94. {
  95. return RepositoryType.MAVEN.equals( type );
  96. }
  97. @Override
  98. public void merge( MetadataRepository metadataRepository, String sourceRepoId, String targetRepoId )
  99. throws RepositoryMergerException
  100. {
  101. try(RepositorySession session = repositorySessionFactory.createSession())
  102. {
  103. List<ArtifactMetadata> artifactsInSourceRepo = metadataRepository.getArtifacts(session , sourceRepoId );
  104. for ( ArtifactMetadata artifactMetadata : artifactsInSourceRepo )
  105. {
  106. artifactMetadata.setRepositoryId( targetRepoId );
  107. createFolderStructure( sourceRepoId, targetRepoId, artifactMetadata );
  108. }
  109. }
  110. catch ( MetadataRepositoryException e )
  111. {
  112. throw new RepositoryMergerException( e.getMessage(), e );
  113. }
  114. catch ( IOException e )
  115. {
  116. throw new RepositoryMergerException( e.getMessage(), e );
  117. }
  118. catch ( RepositoryException e )
  119. {
  120. throw new RepositoryMergerException( e.getMessage(), e );
  121. }
  122. }
  123. // TODO when UI needs a subset to merge
  124. @Override
  125. public void merge( MetadataRepository metadataRepository, String sourceRepoId, String targetRepoId,
  126. Filter<ArtifactMetadata> filter )
  127. throws RepositoryMergerException
  128. {
  129. try(RepositorySession session = repositorySessionFactory.createSession())
  130. {
  131. List<ArtifactMetadata> sourceArtifacts = metadataRepository.getArtifacts(session , sourceRepoId );
  132. for ( ArtifactMetadata metadata : sourceArtifacts )
  133. {
  134. if ( filter.accept( metadata ) )
  135. {
  136. createFolderStructure( sourceRepoId, targetRepoId, metadata );
  137. }
  138. }
  139. }
  140. catch ( MetadataRepositoryException e )
  141. {
  142. throw new RepositoryMergerException( e.getMessage(), e );
  143. }
  144. catch ( IOException e )
  145. {
  146. throw new RepositoryMergerException( e.getMessage(), e );
  147. }
  148. catch ( RepositoryException e )
  149. {
  150. throw new RepositoryMergerException( e.getMessage(), e );
  151. }
  152. }
  153. private void createFolderStructure( String sourceRepoId, String targetRepoId, ArtifactMetadata artifactMetadata )
  154. throws IOException, RepositoryException
  155. {
  156. Configuration config = configuration.getConfiguration();
  157. ManagedRepositoryConfiguration targetRepoConfig = config.findManagedRepositoryById( targetRepoId );
  158. ManagedRepositoryConfiguration sourceRepoConfig = config.findManagedRepositoryById( sourceRepoId );
  159. Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
  160. TimeZone timezone = TimeZone.getTimeZone( "UTC" );
  161. DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
  162. fmt.setTimeZone( timezone );
  163. String timestamp = fmt.format( lastUpdatedTimestamp );
  164. String targetRepoPath = targetRepoConfig.getLocation();
  165. String sourceRepoPath = sourceRepoConfig.getLocation();
  166. String artifactPath = pathTranslator.toPath( artifactMetadata.getNamespace(), artifactMetadata.getProject(),
  167. artifactMetadata.getProjectVersion(), artifactMetadata.getId() );
  168. Path sourceArtifactFile = Paths.get( sourceRepoPath, artifactPath );
  169. Path targetArtifactFile = Paths.get( targetRepoPath, artifactPath );
  170. log.debug( "artifactPath {}", artifactPath );
  171. int lastIndex = artifactPath.lastIndexOf( RepositoryPathTranslator.PATH_SEPARATOR );
  172. Path targetFile = Paths.get( targetRepoPath, artifactPath.substring( 0, lastIndex ) );
  173. if ( !Files.exists(targetFile) )
  174. {
  175. // create the folder structure when it does not exist
  176. Files.createDirectories(targetFile);
  177. }
  178. // artifact copying
  179. copyFile( sourceArtifactFile, targetArtifactFile );
  180. // pom file copying
  181. // TODO need to use path translator to get the pom file path
  182. // String fileName = artifactMetadata.getProject() + "-" + artifactMetadata.getVersion() + ".pom";
  183. //
  184. // File sourcePomFile =
  185. // pathTranslator.toFile( new File( sourceRepoPath ), artifactMetadata.getId(), artifactMetadata.getProject(),
  186. // artifactMetadata.getVersion(), fileName );
  187. //
  188. // String relativePathToPomFile = sourcePomFile.getAbsolutePath().split( sourceRepoPath )[1];
  189. // File targetPomFile = new File( targetRepoPath, relativePathToPomFile );
  190. //pom file copying (file path is taken with out using path translator)
  191. String index = artifactPath.substring( lastIndex + 1 );
  192. int last = index.lastIndexOf( '.' );
  193. Path sourcePomFile = Paths.get( sourceRepoPath,
  194. artifactPath.substring( 0, lastIndex ) + "/" + artifactPath.substring(
  195. lastIndex + 1 ).substring( 0, last ) + ".pom" );
  196. Path targetPomFile = Paths.get( targetRepoPath,
  197. artifactPath.substring( 0, lastIndex ) + "/" + artifactPath.substring(
  198. lastIndex + 1 ).substring( 0, last ) + ".pom" );
  199. if ( !Files.exists(targetPomFile) && Files.exists(sourcePomFile) )
  200. {
  201. copyFile( sourcePomFile, targetPomFile );
  202. }
  203. // explicitly update only if metadata-updater consumer is not enabled!
  204. if ( !config.getRepositoryScanning().getKnownContentConsumers().contains( "metadata-updater" ) )
  205. {
  206. // updating version metadata files
  207. FilesystemStorage fsStorage = new FilesystemStorage(Paths.get(sourceRepoPath), new DefaultFileLockManager());
  208. StorageAsset versionMetaDataFileInSourceRepo =
  209. pathTranslator.toFile( new FilesystemAsset(fsStorage, "", Paths.get(sourceRepoPath)), artifactMetadata.getNamespace(),
  210. artifactMetadata.getProject(), artifactMetadata.getVersion(),
  211. METADATA_FILENAME );
  212. if ( versionMetaDataFileInSourceRepo.exists() )
  213. {//Pattern quote for windows path
  214. String relativePathToVersionMetadataFile =
  215. getRelativeAssetPath(versionMetaDataFileInSourceRepo);
  216. Path versionMetaDataFileInTargetRepo = Paths.get( targetRepoPath, relativePathToVersionMetadataFile );
  217. if ( !Files.exists(versionMetaDataFileInTargetRepo) )
  218. {
  219. copyFile( versionMetaDataFileInSourceRepo.getFilePath(), versionMetaDataFileInTargetRepo );
  220. }
  221. else
  222. {
  223. updateVersionMetadata( versionMetaDataFileInTargetRepo, artifactMetadata, lastUpdatedTimestamp );
  224. }
  225. }
  226. // updating project meta data file
  227. StorageAsset projectDirectoryInSourceRepo = versionMetaDataFileInSourceRepo.getParent().getParent();
  228. StorageAsset projectMetadataFileInSourceRepo = projectDirectoryInSourceRepo.resolve(METADATA_FILENAME );
  229. if ( projectMetadataFileInSourceRepo.exists() )
  230. {
  231. String relativePathToProjectMetadataFile =
  232. getRelativeAssetPath(projectMetadataFileInSourceRepo);
  233. Path projectMetadataFileInTargetRepo = Paths.get( targetRepoPath, relativePathToProjectMetadataFile );
  234. if ( !Files.exists(projectMetadataFileInTargetRepo) )
  235. {
  236. copyFile( projectMetadataFileInSourceRepo.getFilePath(), projectMetadataFileInTargetRepo );
  237. }
  238. else
  239. {
  240. updateProjectMetadata( projectMetadataFileInTargetRepo, artifactMetadata, lastUpdatedTimestamp,
  241. timestamp );
  242. }
  243. }
  244. }
  245. }
  246. private String getRelativeAssetPath(final StorageAsset asset) {
  247. String relPath = asset.getPath();
  248. while(relPath.startsWith("/")) {
  249. relPath = relPath.substring(1);
  250. }
  251. return relPath;
  252. }
  253. private void copyFile( Path sourceFile, Path targetFile )
  254. throws IOException
  255. {
  256. FileUtils.copyFile( sourceFile.toFile(), targetFile.toFile() );
  257. }
  258. private void updateProjectMetadata( Path projectMetaDataFileIntargetRepo, ArtifactMetadata artifactMetadata,
  259. Date lastUpdatedTimestamp, String timestamp )
  260. throws RepositoryMetadataException
  261. {
  262. ArrayList<String> availableVersions = new ArrayList<>();
  263. String latestVersion = artifactMetadata.getProjectVersion();
  264. ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetaDataFileIntargetRepo );
  265. if ( Files.exists(projectMetaDataFileIntargetRepo) )
  266. {
  267. availableVersions = (ArrayList<String>) projectMetadata.getAvailableVersions();
  268. Collections.sort( availableVersions, VersionComparator.getInstance() );
  269. if ( !availableVersions.contains( artifactMetadata.getVersion() ) )
  270. {
  271. availableVersions.add( artifactMetadata.getVersion() );
  272. }
  273. latestVersion = availableVersions.get( availableVersions.size() - 1 );
  274. }
  275. else
  276. {
  277. availableVersions.add( artifactMetadata.getProjectVersion() );
  278. projectMetadata.setGroupId( artifactMetadata.getNamespace() );
  279. projectMetadata.setArtifactId( artifactMetadata.getProject() );
  280. }
  281. if ( projectMetadata.getGroupId() == null )
  282. {
  283. projectMetadata.setGroupId( artifactMetadata.getNamespace() );
  284. }
  285. if ( projectMetadata.getArtifactId() == null )
  286. {
  287. projectMetadata.setArtifactId( artifactMetadata.getProject() );
  288. }
  289. projectMetadata.setLatestVersion( latestVersion );
  290. projectMetadata.setAvailableVersions( availableVersions );
  291. projectMetadata.setLastUpdated( timestamp );
  292. projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
  293. if ( !VersionUtil.isSnapshot( artifactMetadata.getVersion() ) )
  294. {
  295. projectMetadata.setReleasedVersion( latestVersion );
  296. }
  297. try(BufferedWriter writer = Files.newBufferedWriter(projectMetaDataFileIntargetRepo)) {
  298. RepositoryMetadataWriter.write( projectMetadata, writer );
  299. } catch (IOException e) {
  300. throw new RepositoryMetadataException(e);
  301. }
  302. }
  303. private void updateVersionMetadata( Path versionMetaDataFileInTargetRepo, ArtifactMetadata artifactMetadata,
  304. Date lastUpdatedTimestamp )
  305. throws RepositoryMetadataException
  306. {
  307. ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetaDataFileInTargetRepo );
  308. if ( !Files.exists(versionMetaDataFileInTargetRepo) )
  309. {
  310. versionMetadata.setGroupId( artifactMetadata.getNamespace() );
  311. versionMetadata.setArtifactId( artifactMetadata.getProject() );
  312. versionMetadata.setVersion( artifactMetadata.getProjectVersion() );
  313. }
  314. versionMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
  315. try(BufferedWriter writer = Files.newBufferedWriter(versionMetaDataFileInTargetRepo) ) {
  316. RepositoryMetadataWriter.write( versionMetadata, writer);
  317. } catch (IOException e) {
  318. throw new RepositoryMetadataException(e);
  319. }
  320. }
  321. private ArchivaRepositoryMetadata getMetadata( Path metadataFile )
  322. throws RepositoryMetadataException
  323. {
  324. ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
  325. if ( Files.exists(metadataFile) )
  326. {
  327. try
  328. {
  329. metadata = MavenMetadataReader.read( metadataFile );
  330. }
  331. catch (XMLException e )
  332. {
  333. throw new RepositoryMetadataException( e.getMessage(), e );
  334. }
  335. }
  336. return metadata;
  337. }
  338. @Override
  339. public List<ArtifactMetadata> getConflictingArtifacts( MetadataRepository metadataRepository, String sourceRepo,
  340. String targetRepo )
  341. throws RepositoryMergerException
  342. {
  343. try(RepositorySession session = repositorySessionFactory.createSession())
  344. {
  345. TreeSet<ArtifactMetadata> targetArtifacts = new TreeSet<>(META_COMPARATOR);
  346. targetArtifacts.addAll(metadataRepository.getArtifacts(session , targetRepo ));
  347. TreeSet<ArtifactMetadata> sourceArtifacts = new TreeSet<>(META_COMPARATOR);
  348. sourceArtifacts.addAll(metadataRepository.getArtifacts(session , sourceRepo ));
  349. sourceArtifacts.retainAll(targetArtifacts);
  350. return new ArrayList<>(sourceArtifacts);
  351. }
  352. catch ( MetadataRepositoryException e )
  353. {
  354. throw new RepositoryMergerException( e.getMessage(), e );
  355. }
  356. }
  357. public RepositorySessionFactory getRepositorySessionFactory( )
  358. {
  359. return repositorySessionFactory;
  360. }
  361. public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory )
  362. {
  363. this.repositorySessionFactory = repositorySessionFactory;
  364. }
  365. }