001 package 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 022 import org.apache.archiva.common.utils.VersionComparator; 023 import org.apache.archiva.common.utils.VersionUtil; 024 import org.apache.archiva.configuration.ArchivaConfiguration; 025 import org.apache.archiva.configuration.Configuration; 026 import org.apache.archiva.configuration.ManagedRepositoryConfiguration; 027 import org.apache.archiva.maven2.metadata.MavenMetadataReader; 028 import org.apache.archiva.metadata.model.ArtifactMetadata; 029 import org.apache.archiva.metadata.repository.MetadataRepository; 030 import org.apache.archiva.metadata.repository.MetadataRepositoryException; 031 import org.apache.archiva.metadata.repository.filter.Filter; 032 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator; 033 import org.apache.archiva.model.ArchivaRepositoryMetadata; 034 import org.apache.archiva.repository.RepositoryException; 035 import org.apache.archiva.repository.metadata.RepositoryMetadataException; 036 import org.apache.archiva.repository.metadata.RepositoryMetadataWriter; 037 import org.apache.archiva.xml.XMLException; 038 import org.apache.commons.io.FileUtils; 039 import org.slf4j.Logger; 040 import org.slf4j.LoggerFactory; 041 import org.springframework.stereotype.Service; 042 043 import javax.inject.Inject; 044 import javax.inject.Named; 045 import java.io.File; 046 import java.io.IOException; 047 import java.text.DateFormat; 048 import java.text.SimpleDateFormat; 049 import java.util.ArrayList; 050 import java.util.Calendar; 051 import java.util.Collections; 052 import java.util.Date; 053 import java.util.List; 054 import java.util.TimeZone; 055 import java.util.regex.Pattern; 056 057 /** 058 * 059 */ 060 @Service ("repositoryMerger#maven2") 061 public 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 public void merge( MetadataRepository metadataRepository, String sourceRepoId, String targetRepoId ) 094 throws RepositoryMergerException 095 { 096 097 try 098 { 099 List<ArtifactMetadata> artifactsInSourceRepo = metadataRepository.getArtifacts( sourceRepoId ); 100 for ( ArtifactMetadata artifactMetadata : artifactsInSourceRepo ) 101 { 102 artifactMetadata.setRepositoryId( targetRepoId ); 103 createFolderStructure( sourceRepoId, targetRepoId, artifactMetadata ); 104 } 105 } 106 catch ( MetadataRepositoryException e ) 107 { 108 throw new RepositoryMergerException( e.getMessage(), e ); 109 } 110 catch ( IOException e ) 111 { 112 throw new RepositoryMergerException( e.getMessage(), e ); 113 } 114 catch ( RepositoryException e ) 115 { 116 throw new RepositoryMergerException( e.getMessage(), e ); 117 } 118 } 119 120 // TODO when UI needs a subset to merge 121 public void merge( MetadataRepository metadataRepository, String sourceRepoId, String targetRepoId, 122 Filter<ArtifactMetadata> filter ) 123 throws RepositoryMergerException 124 { 125 try 126 { 127 List<ArtifactMetadata> sourceArtifacts = metadataRepository.getArtifacts( sourceRepoId ); 128 for ( ArtifactMetadata metadata : sourceArtifacts ) 129 { 130 if ( filter.accept( metadata ) ) 131 { 132 createFolderStructure( sourceRepoId, targetRepoId, metadata ); 133 } 134 } 135 } 136 catch ( MetadataRepositoryException e ) 137 { 138 throw new RepositoryMergerException( e.getMessage(), e ); 139 } 140 catch ( IOException e ) 141 { 142 throw new RepositoryMergerException( e.getMessage(), e ); 143 } 144 catch ( RepositoryException e ) 145 { 146 throw new RepositoryMergerException( e.getMessage(), e ); 147 } 148 } 149 150 private void createFolderStructure( String sourceRepoId, String targetRepoId, ArtifactMetadata artifactMetadata ) 151 throws IOException, RepositoryException 152 { 153 Configuration config = configuration.getConfiguration(); 154 155 ManagedRepositoryConfiguration targetRepoConfig = config.findManagedRepositoryById( targetRepoId ); 156 157 ManagedRepositoryConfiguration sourceRepoConfig = config.findManagedRepositoryById( sourceRepoId ); 158 159 Date lastUpdatedTimestamp = Calendar.getInstance().getTime(); 160 161 TimeZone timezone = TimeZone.getTimeZone( "UTC" ); 162 163 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" ); 164 165 fmt.setTimeZone( timezone ); 166 167 String timestamp = fmt.format( lastUpdatedTimestamp ); 168 169 String targetRepoPath = targetRepoConfig.getLocation(); 170 171 String sourceRepoPath = sourceRepoConfig.getLocation(); 172 173 String artifactPath = pathTranslator.toPath( artifactMetadata.getNamespace(), artifactMetadata.getProject(), 174 artifactMetadata.getProjectVersion(), artifactMetadata.getId() ); 175 176 File sourceArtifactFile = new File( sourceRepoPath, artifactPath ); 177 178 File targetArtifactFile = new File( targetRepoPath, artifactPath ); 179 180 log.debug( "artifactPath {}", artifactPath ); 181 182 int lastIndex = artifactPath.lastIndexOf( RepositoryPathTranslator.PATH_SEPARATOR ); 183 184 File targetFile = new File( targetRepoPath, artifactPath.substring( 0, lastIndex ) ); 185 186 if ( !targetFile.exists() ) 187 { 188 // create the folder structure when it does not exist 189 targetFile.mkdirs(); 190 } 191 // artifact copying 192 copyFile( sourceArtifactFile, targetArtifactFile ); 193 194 // pom file copying 195 String fileName = artifactMetadata.getProject() + "-" + artifactMetadata.getVersion() + ".pom"; 196 197 // pom file copying 198 // TODO need to use path translator to get the pom file path 199 // String fileName = artifactMetadata.getProject() + "-" + artifactMetadata.getVersion() + ".pom"; 200 // 201 // File sourcePomFile = 202 // pathTranslator.toFile( new File( sourceRepoPath ), artifactMetadata.getId(), artifactMetadata.getProject(), 203 // artifactMetadata.getVersion(), fileName ); 204 // 205 // String relativePathToPomFile = sourcePomFile.getAbsolutePath().split( sourceRepoPath )[1]; 206 // File targetPomFile = new File( targetRepoPath, relativePathToPomFile ); 207 208 //pom file copying (file path is taken with out using path translator) 209 210 String index = artifactPath.substring( lastIndex + 1 ); 211 int last = index.lastIndexOf( '.' ); 212 File sourcePomFile = new File( sourceRepoPath, 213 artifactPath.substring( 0, lastIndex ) + "/" + artifactPath.substring( 214 lastIndex + 1 ).substring( 0, last ) + ".pom" ); 215 File targetPomFile = new File( targetRepoPath, 216 artifactPath.substring( 0, lastIndex ) + "/" + artifactPath.substring( 217 lastIndex + 1 ).substring( 0, last ) + ".pom" ); 218 219 if ( !targetPomFile.exists() && sourcePomFile.exists() ) 220 { 221 copyFile( sourcePomFile, targetPomFile ); 222 } 223 224 // explicitly update only if metadata-updater consumer is not enabled! 225 if ( !config.getRepositoryScanning().getKnownContentConsumers().contains( "metadata-updater" ) ) 226 { 227 228 // updating version metadata files 229 File versionMetaDataFileInSourceRepo = 230 pathTranslator.toFile( new File( sourceRepoPath ), artifactMetadata.getNamespace(), 231 artifactMetadata.getProject(), artifactMetadata.getVersion(), 232 METADATA_FILENAME ); 233 234 if ( versionMetaDataFileInSourceRepo.exists() ) 235 {//Pattern quote for windows path 236 String relativePathToVersionMetadataFile = 237 versionMetaDataFileInSourceRepo.getAbsolutePath().split( Pattern.quote( sourceRepoPath ) )[1]; 238 File versionMetaDataFileInTargetRepo = new File( targetRepoPath, relativePathToVersionMetadataFile ); 239 240 if ( !versionMetaDataFileInTargetRepo.exists() ) 241 { 242 copyFile( versionMetaDataFileInSourceRepo, versionMetaDataFileInTargetRepo ); 243 } 244 else 245 { 246 updateVersionMetadata( versionMetaDataFileInTargetRepo, artifactMetadata, lastUpdatedTimestamp ); 247 248 } 249 } 250 251 // updating project meta data file 252 String projectDirectoryInSourceRepo = new File( versionMetaDataFileInSourceRepo.getParent() ).getParent(); 253 File projectMetadataFileInSourceRepo = new File( projectDirectoryInSourceRepo, METADATA_FILENAME ); 254 255 if ( projectMetadataFileInSourceRepo.exists() ) 256 { 257 String relativePathToProjectMetadataFile = 258 projectMetadataFileInSourceRepo.getAbsolutePath().split( Pattern.quote( sourceRepoPath ) )[1]; 259 File projectMetadataFileInTargetRepo = new File( targetRepoPath, relativePathToProjectMetadataFile ); 260 261 if ( !projectMetadataFileInTargetRepo.exists() ) 262 { 263 264 copyFile( projectMetadataFileInSourceRepo, projectMetadataFileInTargetRepo ); 265 } 266 else 267 { 268 updateProjectMetadata( projectMetadataFileInTargetRepo, artifactMetadata, lastUpdatedTimestamp, 269 timestamp ); 270 } 271 } 272 } 273 274 } 275 276 private void copyFile( File sourceFile, File targetFile ) 277 throws IOException 278 { 279 280 FileUtils.copyFile( sourceFile, targetFile ); 281 282 } 283 284 private void updateProjectMetadata( File projectMetaDataFileIntargetRepo, ArtifactMetadata artifactMetadata, 285 Date lastUpdatedTimestamp, String timestamp ) 286 throws RepositoryMetadataException 287 { 288 ArrayList<String> availableVersions = new ArrayList<String>(); 289 String latestVersion = artifactMetadata.getProjectVersion(); 290 291 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetaDataFileIntargetRepo ); 292 293 if ( projectMetaDataFileIntargetRepo.exists() ) 294 { 295 availableVersions = (ArrayList<String>) projectMetadata.getAvailableVersions(); 296 297 Collections.sort( availableVersions, VersionComparator.getInstance() ); 298 299 if ( !availableVersions.contains( artifactMetadata.getVersion() ) ) 300 { 301 availableVersions.add( artifactMetadata.getVersion() ); 302 } 303 304 latestVersion = availableVersions.get( availableVersions.size() - 1 ); 305 } 306 else 307 { 308 availableVersions.add( artifactMetadata.getProjectVersion() ); 309 projectMetadata.setGroupId( artifactMetadata.getNamespace() ); 310 projectMetadata.setArtifactId( artifactMetadata.getProject() ); 311 } 312 313 if ( projectMetadata.getGroupId() == null ) 314 { 315 projectMetadata.setGroupId( artifactMetadata.getNamespace() ); 316 } 317 318 if ( projectMetadata.getArtifactId() == null ) 319 { 320 projectMetadata.setArtifactId( artifactMetadata.getProject() ); 321 } 322 323 projectMetadata.setLatestVersion( latestVersion ); 324 projectMetadata.setAvailableVersions( availableVersions ); 325 projectMetadata.setLastUpdated( timestamp ); 326 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp ); 327 328 if ( !VersionUtil.isSnapshot( artifactMetadata.getVersion() ) ) 329 { 330 projectMetadata.setReleasedVersion( latestVersion ); 331 } 332 333 RepositoryMetadataWriter.write( projectMetadata, projectMetaDataFileIntargetRepo ); 334 335 } 336 337 private void updateVersionMetadata( File versionMetaDataFileInTargetRepo, ArtifactMetadata artifactMetadata, 338 Date lastUpdatedTimestamp ) 339 throws RepositoryMetadataException 340 { 341 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetaDataFileInTargetRepo ); 342 if ( !versionMetaDataFileInTargetRepo.exists() ) 343 { 344 versionMetadata.setGroupId( artifactMetadata.getNamespace() ); 345 versionMetadata.setArtifactId( artifactMetadata.getProject() ); 346 versionMetadata.setVersion( artifactMetadata.getProjectVersion() ); 347 } 348 349 versionMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp ); 350 RepositoryMetadataWriter.write( versionMetadata, versionMetaDataFileInTargetRepo ); 351 } 352 353 private ArchivaRepositoryMetadata getMetadata( File metadataFile ) 354 throws RepositoryMetadataException 355 { 356 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata(); 357 if ( metadataFile.exists() ) 358 { 359 try 360 { 361 metadata = MavenMetadataReader.read( metadataFile ); 362 } 363 catch ( XMLException e ) 364 { 365 throw new RepositoryMetadataException( e.getMessage(), e ); 366 } 367 } 368 return metadata; 369 } 370 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<ArtifactMetadata>(); 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 }