1 package org.apache.archiva.maven.repository.merge;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
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
21 import org.apache.archiva.common.filelock.DefaultFileLockManager;
22 import org.apache.archiva.common.utils.VersionComparator;
23 import org.apache.archiva.common.utils.VersionUtil;
24 import org.apache.archiva.configuration.ArchivaConfiguration;
25 import org.apache.archiva.configuration.Configuration;
26 import org.apache.archiva.configuration.ManagedRepositoryConfiguration;
27 import org.apache.archiva.filter.Filter;
28 import org.apache.archiva.maven.metadata.MavenMetadataReader;
29 import org.apache.archiva.metadata.model.ArtifactMetadata;
30 import org.apache.archiva.metadata.repository.MetadataRepository;
31 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
32 import org.apache.archiva.metadata.repository.RepositorySession;
33 import org.apache.archiva.metadata.repository.RepositorySessionFactory;
34 import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
35 import org.apache.archiva.model.ArchivaRepositoryMetadata;
36 import org.apache.archiva.repository.RepositoryException;
37 import org.apache.archiva.repository.RepositoryType;
38 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
39 import org.apache.archiva.repository.metadata.base.RepositoryMetadataWriter;
40 import org.apache.archiva.repository.storage.StorageAsset;
41 import org.apache.archiva.repository.storage.fs.FilesystemAsset;
42 import org.apache.archiva.repository.storage.fs.FilesystemStorage;
43 import org.apache.archiva.stagerepository.merge.RepositoryMerger;
44 import org.apache.archiva.stagerepository.merge.RepositoryMergerException;
45 import org.apache.commons.io.FileUtils;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.springframework.stereotype.Service;
50 import javax.inject.Inject;
51 import javax.inject.Named;
52 import java.io.BufferedWriter;
53 import java.io.IOException;
54 import java.nio.file.Files;
55 import java.nio.file.Path;
56 import java.nio.file.Paths;
57 import java.text.DateFormat;
58 import java.text.SimpleDateFormat;
59 import java.util.ArrayList;
60 import java.util.Calendar;
61 import java.util.Collections;
62 import java.util.Comparator;
63 import java.util.Date;
64 import java.util.List;
65 import java.util.TimeZone;
66 import java.util.TreeSet;
71 @Service ("repositoryMerger#maven2")
72 public class Maven2RepositoryMerger
73 implements RepositoryMerger
77 @Named("metadataReader#maven")
78 private MavenMetadataReader metadataReader;
80 private static final Logger log = LoggerFactory.getLogger( Maven2RepositoryMerger.class );
82 private static final Comparator<ArtifactMetadata> META_COMPARATOR = Comparator.comparing(ArtifactMetadata::getNamespace)
83 .thenComparing(ArtifactMetadata::getProject)
84 .thenComparing(ArtifactMetadata::getId)
85 .thenComparing(ArtifactMetadata::getVersion);
90 private ArchivaConfiguration configuration;
95 private RepositoryPathTranslator pathTranslator;
97 private static final String METADATA_FILENAME = "maven-metadata.xml";
100 private RepositorySessionFactory repositorySessionFactory;
103 public Maven2RepositoryMerger(
104 @Named (value = "archivaConfiguration#default") ArchivaConfiguration archivaConfiguration,
105 @Named (value = "repositoryPathTranslator#maven2") RepositoryPathTranslator repositoryPathTranslator )
107 this.configuration = archivaConfiguration;
108 this.pathTranslator = repositoryPathTranslator;
111 public void setConfiguration( ArchivaConfiguration configuration )
113 this.configuration = configuration;
117 public boolean supportsRepository( RepositoryType type )
119 return RepositoryType.MAVEN.equals( type );
123 public void merge( MetadataRepository metadataRepository, String sourceRepoId, String targetRepoId )
124 throws RepositoryMergerException
127 try(RepositorySession session = repositorySessionFactory.createSession())
129 List<ArtifactMetadata> artifactsInSourceRepo = metadataRepository.getArtifacts(session , sourceRepoId );
130 for ( ArtifactMetadata artifactMetadata : artifactsInSourceRepo )
132 artifactMetadata.setRepositoryId( targetRepoId );
133 createFolderStructure( sourceRepoId, targetRepoId, artifactMetadata );
136 catch ( MetadataRepositoryException e )
138 throw new RepositoryMergerException( e.getMessage(), e );
140 catch ( IOException e )
142 throw new RepositoryMergerException( e.getMessage(), e );
144 catch ( RepositoryException e )
146 throw new RepositoryMergerException( e.getMessage(), e );
150 // TODO when UI needs a subset to merge
152 public void merge( MetadataRepository metadataRepository, String sourceRepoId, String targetRepoId,
153 Filter<ArtifactMetadata> filter )
154 throws RepositoryMergerException
156 try(RepositorySession session = repositorySessionFactory.createSession())
158 List<ArtifactMetadata> sourceArtifacts = metadataRepository.getArtifacts(session , sourceRepoId );
159 for ( ArtifactMetadata metadata : sourceArtifacts )
161 if ( filter.accept( metadata ) )
163 createFolderStructure( sourceRepoId, targetRepoId, metadata );
167 catch ( MetadataRepositoryException e )
169 throw new RepositoryMergerException( e.getMessage(), e );
171 catch ( IOException e )
173 throw new RepositoryMergerException( e.getMessage(), e );
175 catch ( RepositoryException e )
177 throw new RepositoryMergerException( e.getMessage(), e );
181 private void createFolderStructure( String sourceRepoId, String targetRepoId, ArtifactMetadata artifactMetadata )
182 throws IOException, RepositoryException
184 Configuration config = configuration.getConfiguration();
186 ManagedRepositoryConfiguration targetRepoConfig = config.findManagedRepositoryById( targetRepoId );
188 ManagedRepositoryConfiguration sourceRepoConfig = config.findManagedRepositoryById( sourceRepoId );
190 Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
192 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
194 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
196 fmt.setTimeZone( timezone );
198 String timestamp = fmt.format( lastUpdatedTimestamp );
200 String targetRepoPath = targetRepoConfig.getLocation();
202 String sourceRepoPath = sourceRepoConfig.getLocation();
204 String artifactPath = pathTranslator.toPath( artifactMetadata.getNamespace(), artifactMetadata.getProject(),
205 artifactMetadata.getProjectVersion(), artifactMetadata.getId() );
207 Path sourceArtifactFile = Paths.get( sourceRepoPath, artifactPath );
209 Path targetArtifactFile = Paths.get( targetRepoPath, artifactPath );
211 log.debug( "artifactPath {}", artifactPath );
213 int lastIndex = artifactPath.lastIndexOf( RepositoryPathTranslator.PATH_SEPARATOR );
215 Path targetFile = Paths.get( targetRepoPath, artifactPath.substring( 0, lastIndex ) );
217 if ( !Files.exists(targetFile) )
219 // create the folder structure when it does not exist
220 Files.createDirectories(targetFile);
223 copyFile( sourceArtifactFile, targetArtifactFile );
226 // TODO need to use path translator to get the pom file path
227 // String fileName = artifactMetadata.getProject() + "-" + artifactMetadata.getVersion() + ".pom";
229 // File sourcePomFile =
230 // pathTranslator.toFile( new File( sourceRepoPath ), artifactMetadata.getId(), artifactMetadata.getProject(),
231 // artifactMetadata.getVersion(), fileName );
233 // String relativePathToPomFile = sourcePomFile.getAbsolutePath().split( sourceRepoPath )[1];
234 // File targetPomFile = new File( targetRepoPath, relativePathToPomFile );
236 //pom file copying (file path is taken with out using path translator)
238 String index = artifactPath.substring( lastIndex + 1 );
239 int last = index.lastIndexOf( '.' );
240 Path sourcePomFile = Paths.get( sourceRepoPath,
241 artifactPath.substring( 0, lastIndex ) + "/" + artifactPath.substring(
242 lastIndex + 1 ).substring( 0, last ) + ".pom" );
243 Path targetPomFile = Paths.get( targetRepoPath,
244 artifactPath.substring( 0, lastIndex ) + "/" + artifactPath.substring(
245 lastIndex + 1 ).substring( 0, last ) + ".pom" );
247 if ( !Files.exists(targetPomFile) && Files.exists(sourcePomFile) )
249 copyFile( sourcePomFile, targetPomFile );
252 // explicitly update only if metadata-updater consumer is not enabled!
253 if ( !config.getRepositoryScanning().getKnownContentConsumers().contains( "metadata-updater" ) )
256 // updating version metadata files
257 FilesystemStorage fsStorage = new FilesystemStorage(Paths.get(sourceRepoPath), new DefaultFileLockManager());
259 StorageAsset versionMetaDataFileInSourceRepo =
260 pathTranslator.toFile( new FilesystemAsset(fsStorage, "", Paths.get(sourceRepoPath)), artifactMetadata.getNamespace(),
261 artifactMetadata.getProject(), artifactMetadata.getVersion(),
264 if ( versionMetaDataFileInSourceRepo.exists() )
265 {//Pattern quote for windows path
266 String relativePathToVersionMetadataFile =
267 getRelativeAssetPath(versionMetaDataFileInSourceRepo);
268 Path versionMetaDataFileInTargetRepo = Paths.get( targetRepoPath, relativePathToVersionMetadataFile );
270 if ( !Files.exists(versionMetaDataFileInTargetRepo) )
272 copyFile( versionMetaDataFileInSourceRepo.getFilePath(), versionMetaDataFileInTargetRepo );
276 updateVersionMetadata( versionMetaDataFileInTargetRepo, artifactMetadata, lastUpdatedTimestamp );
281 // updating project meta data file
282 StorageAsset projectDirectoryInSourceRepo = versionMetaDataFileInSourceRepo.getParent().getParent();
283 StorageAsset projectMetadataFileInSourceRepo = projectDirectoryInSourceRepo.resolve(METADATA_FILENAME );
285 if ( projectMetadataFileInSourceRepo.exists() )
287 String relativePathToProjectMetadataFile =
288 getRelativeAssetPath(projectMetadataFileInSourceRepo);
289 Path projectMetadataFileInTargetRepo = Paths.get( targetRepoPath, relativePathToProjectMetadataFile );
291 if ( !Files.exists(projectMetadataFileInTargetRepo) )
294 copyFile( projectMetadataFileInSourceRepo.getFilePath(), projectMetadataFileInTargetRepo );
298 updateProjectMetadata( projectMetadataFileInTargetRepo, artifactMetadata, lastUpdatedTimestamp,
306 private String getRelativeAssetPath(final StorageAsset asset) {
307 String relPath = asset.getPath();
308 while(relPath.startsWith("/")) {
309 relPath = relPath.substring(1);
314 private void copyFile( Path sourceFile, Path targetFile )
318 FileUtils.copyFile( sourceFile.toFile(), targetFile.toFile() );
322 private void updateProjectMetadata( Path projectMetaDataFileIntargetRepo, ArtifactMetadata artifactMetadata,
323 Date lastUpdatedTimestamp, String timestamp )
324 throws RepositoryMetadataException
326 ArrayList<String> availableVersions = new ArrayList<>();
327 String latestVersion = artifactMetadata.getProjectVersion();
329 ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetaDataFileIntargetRepo );
331 if ( Files.exists(projectMetaDataFileIntargetRepo) )
333 availableVersions = (ArrayList<String>) projectMetadata.getAvailableVersions();
335 Collections.sort( availableVersions, VersionComparator.getInstance() );
337 if ( !availableVersions.contains( artifactMetadata.getVersion() ) )
339 availableVersions.add( artifactMetadata.getVersion() );
342 latestVersion = availableVersions.get( availableVersions.size() - 1 );
346 availableVersions.add( artifactMetadata.getProjectVersion() );
347 projectMetadata.setGroupId( artifactMetadata.getNamespace() );
348 projectMetadata.setArtifactId( artifactMetadata.getProject() );
351 if ( projectMetadata.getGroupId() == null )
353 projectMetadata.setGroupId( artifactMetadata.getNamespace() );
356 if ( projectMetadata.getArtifactId() == null )
358 projectMetadata.setArtifactId( artifactMetadata.getProject() );
361 projectMetadata.setLatestVersion( latestVersion );
362 projectMetadata.setAvailableVersions( availableVersions );
363 projectMetadata.setLastUpdated( timestamp );
364 projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
366 if ( !VersionUtil.isSnapshot( artifactMetadata.getVersion() ) )
368 projectMetadata.setReleasedVersion( latestVersion );
371 try(BufferedWriter writer = Files.newBufferedWriter(projectMetaDataFileIntargetRepo)) {
372 RepositoryMetadataWriter.write( projectMetadata, writer );
373 } catch (IOException e) {
374 throw new RepositoryMetadataException(e);
379 private void updateVersionMetadata( Path versionMetaDataFileInTargetRepo, ArtifactMetadata artifactMetadata,
380 Date lastUpdatedTimestamp )
381 throws RepositoryMetadataException
383 ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetaDataFileInTargetRepo );
384 if ( !Files.exists(versionMetaDataFileInTargetRepo) )
386 versionMetadata.setGroupId( artifactMetadata.getNamespace() );
387 versionMetadata.setArtifactId( artifactMetadata.getProject() );
388 versionMetadata.setVersion( artifactMetadata.getProjectVersion() );
391 versionMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
392 try(BufferedWriter writer = Files.newBufferedWriter(versionMetaDataFileInTargetRepo) ) {
393 RepositoryMetadataWriter.write( versionMetadata, writer);
394 } catch (IOException e) {
395 throw new RepositoryMetadataException(e);
399 private ArchivaRepositoryMetadata getMetadata( Path metadataFile )
400 throws RepositoryMetadataException
402 ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
403 if ( Files.exists(metadataFile) )
405 metadata = metadataReader.read( metadataFile );
411 public List<ArtifactMetadata> getConflictingArtifacts( MetadataRepository metadataRepository, String sourceRepo,
413 throws RepositoryMergerException
415 try(RepositorySession session = repositorySessionFactory.createSession())
417 TreeSet<ArtifactMetadata> targetArtifacts = new TreeSet<>(META_COMPARATOR);
418 targetArtifacts.addAll(metadataRepository.getArtifacts(session , targetRepo ));
419 TreeSet<ArtifactMetadata> sourceArtifacts = new TreeSet<>(META_COMPARATOR);
420 sourceArtifacts.addAll(metadataRepository.getArtifacts(session , sourceRepo ));
421 sourceArtifacts.retainAll(targetArtifacts);
423 return new ArrayList<>(sourceArtifacts);
425 catch ( MetadataRepositoryException e )
427 throw new RepositoryMergerException( e.getMessage(), e );
431 public RepositorySessionFactory getRepositorySessionFactory( )
433 return repositorySessionFactory;
436 public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory )
438 this.repositorySessionFactory = repositorySessionFactory;