001package org.apache.archiva.stagerepository.merge; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import org.apache.archiva.common.utils.VersionComparator; 023import org.apache.archiva.common.utils.VersionUtil; 024import org.apache.archiva.configuration.ArchivaConfiguration; 025import org.apache.archiva.configuration.Configuration; 026import org.apache.archiva.configuration.ManagedRepositoryConfiguration; 027import org.apache.archiva.maven2.metadata.MavenMetadataReader; 028import org.apache.archiva.metadata.model.ArtifactMetadata; 029import org.apache.archiva.metadata.repository.MetadataRepository; 030import org.apache.archiva.metadata.repository.MetadataRepositoryException; 031import org.apache.archiva.metadata.repository.filter.Filter; 032import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator; 033import org.apache.archiva.model.ArchivaRepositoryMetadata; 034import org.apache.archiva.repository.RepositoryException; 035import org.apache.archiva.repository.metadata.RepositoryMetadataException; 036import org.apache.archiva.repository.metadata.RepositoryMetadataWriter; 037import org.apache.archiva.xml.XMLException; 038import org.apache.commons.io.FileUtils; 039import org.slf4j.Logger; 040import org.slf4j.LoggerFactory; 041import org.springframework.stereotype.Service; 042 043import javax.inject.Inject; 044import javax.inject.Named; 045import java.io.File; 046import java.io.IOException; 047import java.text.DateFormat; 048import java.text.SimpleDateFormat; 049import java.util.ArrayList; 050import java.util.Calendar; 051import java.util.Collections; 052import java.util.Date; 053import java.util.List; 054import java.util.TimeZone; 055import java.util.regex.Pattern; 056 057/** 058 * 059 */ 060@Service ("repositoryMerger#maven2") 061public class Maven2RepositoryMerger 062 implements RepositoryMerger 063{ 064 065 private Logger log = LoggerFactory.getLogger( getClass() ); 066 067 /** 068 * 069 */ 070 private ArchivaConfiguration configuration; 071 072 /** 073 * 074 */ 075 private RepositoryPathTranslator pathTranslator; 076 077 private static final String METADATA_FILENAME = "maven-metadata.xml"; 078 079 @Inject 080 public Maven2RepositoryMerger( 081 @Named (value = "archivaConfiguration#default") ArchivaConfiguration archivaConfiguration, 082 @Named (value = "repositoryPathTranslator#maven2") RepositoryPathTranslator repositoryPathTranslator ) 083 { 084 this.configuration = archivaConfiguration; 085 this.pathTranslator = repositoryPathTranslator; 086 } 087 088 public void setConfiguration( ArchivaConfiguration configuration ) 089 { 090 this.configuration = configuration; 091 } 092 093 @Override 094 public void merge( MetadataRepository metadataRepository, String sourceRepoId, String targetRepoId ) 095 throws RepositoryMergerException 096 { 097 098 try 099 { 100 List<ArtifactMetadata> artifactsInSourceRepo = metadataRepository.getArtifacts( sourceRepoId ); 101 for ( ArtifactMetadata artifactMetadata : artifactsInSourceRepo ) 102 { 103 artifactMetadata.setRepositoryId( targetRepoId ); 104 createFolderStructure( sourceRepoId, targetRepoId, artifactMetadata ); 105 } 106 } 107 catch ( MetadataRepositoryException e ) 108 { 109 throw new RepositoryMergerException( e.getMessage(), e ); 110 } 111 catch ( IOException e ) 112 { 113 throw new RepositoryMergerException( e.getMessage(), e ); 114 } 115 catch ( RepositoryException e ) 116 { 117 throw new RepositoryMergerException( e.getMessage(), e ); 118 } 119 } 120 121 // TODO when UI needs a subset to merge 122 @Override 123 public void merge( MetadataRepository metadataRepository, String sourceRepoId, String targetRepoId, 124 Filter<ArtifactMetadata> filter ) 125 throws RepositoryMergerException 126 { 127 try 128 { 129 List<ArtifactMetadata> sourceArtifacts = metadataRepository.getArtifacts( sourceRepoId ); 130 for ( ArtifactMetadata metadata : sourceArtifacts ) 131 { 132 if ( filter.accept( metadata ) ) 133 { 134 createFolderStructure( sourceRepoId, targetRepoId, metadata ); 135 } 136 } 137 } 138 catch ( MetadataRepositoryException e ) 139 { 140 throw new RepositoryMergerException( e.getMessage(), e ); 141 } 142 catch ( IOException e ) 143 { 144 throw new RepositoryMergerException( e.getMessage(), e ); 145 } 146 catch ( RepositoryException e ) 147 { 148 throw new RepositoryMergerException( e.getMessage(), e ); 149 } 150 } 151 152 private void createFolderStructure( String sourceRepoId, String targetRepoId, ArtifactMetadata artifactMetadata ) 153 throws IOException, RepositoryException 154 { 155 Configuration config = configuration.getConfiguration(); 156 157 ManagedRepositoryConfiguration targetRepoConfig = config.findManagedRepositoryById( targetRepoId ); 158 159 ManagedRepositoryConfiguration sourceRepoConfig = config.findManagedRepositoryById( sourceRepoId ); 160 161 Date lastUpdatedTimestamp = Calendar.getInstance().getTime(); 162 163 TimeZone timezone = TimeZone.getTimeZone( "UTC" ); 164 165 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" ); 166 167 fmt.setTimeZone( timezone ); 168 169 String timestamp = fmt.format( lastUpdatedTimestamp ); 170 171 String targetRepoPath = targetRepoConfig.getLocation(); 172 173 String sourceRepoPath = sourceRepoConfig.getLocation(); 174 175 String artifactPath = pathTranslator.toPath( artifactMetadata.getNamespace(), artifactMetadata.getProject(), 176 artifactMetadata.getProjectVersion(), artifactMetadata.getId() ); 177 178 File sourceArtifactFile = new File( sourceRepoPath, artifactPath ); 179 180 File targetArtifactFile = new File( targetRepoPath, artifactPath ); 181 182 log.debug( "artifactPath {}", artifactPath ); 183 184 int lastIndex = artifactPath.lastIndexOf( RepositoryPathTranslator.PATH_SEPARATOR ); 185 186 File targetFile = new File( targetRepoPath, artifactPath.substring( 0, lastIndex ) ); 187 188 if ( !targetFile.exists() ) 189 { 190 // create the folder structure when it does not exist 191 targetFile.mkdirs(); 192 } 193 // artifact copying 194 copyFile( sourceArtifactFile, targetArtifactFile ); 195 196 // pom file copying 197 // TODO need to use path translator to get the pom file path 198// String fileName = artifactMetadata.getProject() + "-" + artifactMetadata.getVersion() + ".pom"; 199// 200// File sourcePomFile = 201// pathTranslator.toFile( new File( sourceRepoPath ), artifactMetadata.getId(), artifactMetadata.getProject(), 202// artifactMetadata.getVersion(), fileName ); 203// 204// String relativePathToPomFile = sourcePomFile.getAbsolutePath().split( sourceRepoPath )[1]; 205// File targetPomFile = new File( targetRepoPath, relativePathToPomFile ); 206 207 //pom file copying (file path is taken with out using path translator) 208 209 String index = artifactPath.substring( lastIndex + 1 ); 210 int last = index.lastIndexOf( '.' ); 211 File sourcePomFile = new File( sourceRepoPath, 212 artifactPath.substring( 0, lastIndex ) + "/" + artifactPath.substring( 213 lastIndex + 1 ).substring( 0, last ) + ".pom" ); 214 File targetPomFile = new File( targetRepoPath, 215 artifactPath.substring( 0, lastIndex ) + "/" + artifactPath.substring( 216 lastIndex + 1 ).substring( 0, last ) + ".pom" ); 217 218 if ( !targetPomFile.exists() && sourcePomFile.exists() ) 219 { 220 copyFile( sourcePomFile, targetPomFile ); 221 } 222 223 // explicitly update only if metadata-updater consumer is not enabled! 224 if ( !config.getRepositoryScanning().getKnownContentConsumers().contains( "metadata-updater" ) ) 225 { 226 227 // updating version metadata files 228 File versionMetaDataFileInSourceRepo = 229 pathTranslator.toFile( new File( sourceRepoPath ), artifactMetadata.getNamespace(), 230 artifactMetadata.getProject(), artifactMetadata.getVersion(), 231 METADATA_FILENAME ); 232 233 if ( versionMetaDataFileInSourceRepo.exists() ) 234 {//Pattern quote for windows path 235 String relativePathToVersionMetadataFile = 236 versionMetaDataFileInSourceRepo.getAbsolutePath().split( Pattern.quote( sourceRepoPath ) )[1]; 237 File versionMetaDataFileInTargetRepo = new File( targetRepoPath, relativePathToVersionMetadataFile ); 238 239 if ( !versionMetaDataFileInTargetRepo.exists() ) 240 { 241 copyFile( versionMetaDataFileInSourceRepo, versionMetaDataFileInTargetRepo ); 242 } 243 else 244 { 245 updateVersionMetadata( versionMetaDataFileInTargetRepo, artifactMetadata, lastUpdatedTimestamp ); 246 247 } 248 } 249 250 // updating project meta data file 251 String projectDirectoryInSourceRepo = new File( versionMetaDataFileInSourceRepo.getParent() ).getParent(); 252 File projectMetadataFileInSourceRepo = new File( projectDirectoryInSourceRepo, METADATA_FILENAME ); 253 254 if ( projectMetadataFileInSourceRepo.exists() ) 255 { 256 String relativePathToProjectMetadataFile = 257 projectMetadataFileInSourceRepo.getAbsolutePath().split( Pattern.quote( sourceRepoPath ) )[1]; 258 File projectMetadataFileInTargetRepo = new File( targetRepoPath, relativePathToProjectMetadataFile ); 259 260 if ( !projectMetadataFileInTargetRepo.exists() ) 261 { 262 263 copyFile( projectMetadataFileInSourceRepo, projectMetadataFileInTargetRepo ); 264 } 265 else 266 { 267 updateProjectMetadata( projectMetadataFileInTargetRepo, artifactMetadata, lastUpdatedTimestamp, 268 timestamp ); 269 } 270 } 271 } 272 273 } 274 275 private void copyFile( File sourceFile, File targetFile ) 276 throws IOException 277 { 278 279 FileUtils.copyFile( sourceFile, targetFile ); 280 281 } 282 283 private void updateProjectMetadata( File projectMetaDataFileIntargetRepo, ArtifactMetadata artifactMetadata, 284 Date lastUpdatedTimestamp, String timestamp ) 285 throws RepositoryMetadataException 286 { 287 ArrayList<String> availableVersions = new ArrayList<>(); 288 String latestVersion = artifactMetadata.getProjectVersion(); 289 290 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetaDataFileIntargetRepo ); 291 292 if ( projectMetaDataFileIntargetRepo.exists() ) 293 { 294 availableVersions = (ArrayList<String>) projectMetadata.getAvailableVersions(); 295 296 Collections.sort( availableVersions, VersionComparator.getInstance() ); 297 298 if ( !availableVersions.contains( artifactMetadata.getVersion() ) ) 299 { 300 availableVersions.add( artifactMetadata.getVersion() ); 301 } 302 303 latestVersion = availableVersions.get( availableVersions.size() - 1 ); 304 } 305 else 306 { 307 availableVersions.add( artifactMetadata.getProjectVersion() ); 308 projectMetadata.setGroupId( artifactMetadata.getNamespace() ); 309 projectMetadata.setArtifactId( artifactMetadata.getProject() ); 310 } 311 312 if ( projectMetadata.getGroupId() == null ) 313 { 314 projectMetadata.setGroupId( artifactMetadata.getNamespace() ); 315 } 316 317 if ( projectMetadata.getArtifactId() == null ) 318 { 319 projectMetadata.setArtifactId( artifactMetadata.getProject() ); 320 } 321 322 projectMetadata.setLatestVersion( latestVersion ); 323 projectMetadata.setAvailableVersions( availableVersions ); 324 projectMetadata.setLastUpdated( timestamp ); 325 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp ); 326 327 if ( !VersionUtil.isSnapshot( artifactMetadata.getVersion() ) ) 328 { 329 projectMetadata.setReleasedVersion( latestVersion ); 330 } 331 332 RepositoryMetadataWriter.write( projectMetadata, projectMetaDataFileIntargetRepo ); 333 334 } 335 336 private void updateVersionMetadata( File versionMetaDataFileInTargetRepo, ArtifactMetadata artifactMetadata, 337 Date lastUpdatedTimestamp ) 338 throws RepositoryMetadataException 339 { 340 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetaDataFileInTargetRepo ); 341 if ( !versionMetaDataFileInTargetRepo.exists() ) 342 { 343 versionMetadata.setGroupId( artifactMetadata.getNamespace() ); 344 versionMetadata.setArtifactId( artifactMetadata.getProject() ); 345 versionMetadata.setVersion( artifactMetadata.getProjectVersion() ); 346 } 347 348 versionMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp ); 349 RepositoryMetadataWriter.write( versionMetadata, versionMetaDataFileInTargetRepo ); 350 } 351 352 private ArchivaRepositoryMetadata getMetadata( File metadataFile ) 353 throws RepositoryMetadataException 354 { 355 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata(); 356 if ( metadataFile.exists() ) 357 { 358 try 359 { 360 metadata = MavenMetadataReader.read( metadataFile ); 361 } 362 catch ( XMLException e ) 363 { 364 throw new RepositoryMetadataException( e.getMessage(), e ); 365 } 366 } 367 return metadata; 368 } 369 370 @Override 371 public List<ArtifactMetadata> getConflictingArtifacts( MetadataRepository metadataRepository, String sourceRepo, 372 String targetRepo ) 373 throws RepositoryMergerException 374 { 375 try 376 { 377 List<ArtifactMetadata> targetArtifacts = metadataRepository.getArtifacts( targetRepo ); 378 List<ArtifactMetadata> sourceArtifacts = metadataRepository.getArtifacts( sourceRepo ); 379 List<ArtifactMetadata> conflictsArtifacts = new ArrayList<>(); 380 381 for ( ArtifactMetadata targetArtifact : targetArtifacts ) 382 { 383 for ( ArtifactMetadata sourceArtifact : sourceArtifacts ) 384 { 385 if ( isEquals( targetArtifact, sourceArtifact ) ) 386 { 387 if ( !conflictsArtifacts.contains( sourceArtifact ) ) 388 { 389 conflictsArtifacts.add( sourceArtifact ); 390 } 391 } 392 } 393 } 394 395 sourceArtifacts.removeAll( conflictsArtifacts ); 396 397 return conflictsArtifacts; 398 } 399 catch ( MetadataRepositoryException e ) 400 { 401 throw new RepositoryMergerException( e.getMessage(), e ); 402 } 403 } 404 405 private boolean isEquals( ArtifactMetadata sourceArtifact, ArtifactMetadata targetArtifact ) 406 { 407 boolean isSame = false; 408 409 if ( ( sourceArtifact.getNamespace().equals( targetArtifact.getNamespace() ) ) 410 && ( sourceArtifact.getProject().equals( targetArtifact.getProject() ) ) && ( sourceArtifact.getId().equals( 411 targetArtifact.getId() ) ) && ( sourceArtifact.getProjectVersion().equals( 412 targetArtifact.getProjectVersion() ) ) ) 413 414 { 415 isSame = true; 416 417 } 418 419 return isSame; 420 } 421}