]> source.dussan.org Git - archiva.git/blob
1f21f6ffac07c2d7105297c1bd233d521fe96623
[archiva.git] /
1 package org.apache.archiva.repository.maven.merge;
2
3 /*
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
11  *
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
18  * under the License.
19  */
20
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;
49
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;
67
68 /**
69  *
70  */
71 @Service ("repositoryMerger#maven2")
72 public class Maven2RepositoryMerger
73     implements RepositoryMerger
74 {
75
76     @Inject
77     @Named("metadataReader#maven")
78     private MavenMetadataReader metadataReader;
79
80     private static final Logger log = LoggerFactory.getLogger( Maven2RepositoryMerger.class );
81
82     private static final Comparator<ArtifactMetadata> META_COMPARATOR = Comparator.comparing(ArtifactMetadata::getNamespace)
83             .thenComparing(ArtifactMetadata::getProject)
84             .thenComparing(ArtifactMetadata::getId)
85             .thenComparing(ArtifactMetadata::getVersion);
86
87     /**
88      *
89      */
90     private ArchivaConfiguration configuration;
91
92     /**
93      *
94      */
95     private RepositoryPathTranslator pathTranslator;
96
97     private static final String METADATA_FILENAME = "maven-metadata.xml";
98
99     @Inject
100     private RepositorySessionFactory repositorySessionFactory;
101
102     @Inject
103     public Maven2RepositoryMerger(
104         @Named (value = "archivaConfiguration#default") ArchivaConfiguration archivaConfiguration,
105         @Named (value = "repositoryPathTranslator#maven2") RepositoryPathTranslator repositoryPathTranslator )
106     {
107         this.configuration = archivaConfiguration;
108         this.pathTranslator = repositoryPathTranslator;
109     }
110
111     public void setConfiguration( ArchivaConfiguration configuration )
112     {
113         this.configuration = configuration;
114     }
115
116     @Override
117     public boolean supportsRepository( RepositoryType type )
118     {
119         return RepositoryType.MAVEN.equals( type );
120     }
121
122     @Override
123     public void merge( MetadataRepository metadataRepository, String sourceRepoId, String targetRepoId )
124         throws RepositoryMergerException
125     {
126
127         try(RepositorySession session = repositorySessionFactory.createSession())
128         {
129             List<ArtifactMetadata> artifactsInSourceRepo = metadataRepository.getArtifacts(session , sourceRepoId );
130             for ( ArtifactMetadata artifactMetadata : artifactsInSourceRepo )
131             {
132                 artifactMetadata.setRepositoryId( targetRepoId );
133                 createFolderStructure( sourceRepoId, targetRepoId, artifactMetadata );
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     // TODO when UI needs a subset to merge
151     @Override
152     public void merge( MetadataRepository metadataRepository, String sourceRepoId, String targetRepoId,
153                        Filter<ArtifactMetadata> filter )
154         throws RepositoryMergerException
155     {
156         try(RepositorySession session = repositorySessionFactory.createSession())
157         {
158             List<ArtifactMetadata> sourceArtifacts = metadataRepository.getArtifacts(session , sourceRepoId );
159             for ( ArtifactMetadata metadata : sourceArtifacts )
160             {
161                 if ( filter.accept( metadata ) )
162                 {
163                     createFolderStructure( sourceRepoId, targetRepoId, metadata );
164                 }
165             }
166         }
167         catch ( MetadataRepositoryException e )
168         {
169             throw new RepositoryMergerException( e.getMessage(), e );
170         }
171         catch ( IOException e )
172         {
173             throw new RepositoryMergerException( e.getMessage(), e );
174         }
175         catch ( RepositoryException e )
176         {
177             throw new RepositoryMergerException( e.getMessage(), e );
178         }
179     }
180
181     private void createFolderStructure( String sourceRepoId, String targetRepoId, ArtifactMetadata artifactMetadata )
182         throws IOException, RepositoryException
183     {
184         Configuration config = configuration.getConfiguration();
185
186         ManagedRepositoryConfiguration targetRepoConfig = config.findManagedRepositoryById( targetRepoId );
187
188         ManagedRepositoryConfiguration sourceRepoConfig = config.findManagedRepositoryById( sourceRepoId );
189
190         Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
191
192         TimeZone timezone = TimeZone.getTimeZone( "UTC" );
193
194         DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
195
196         fmt.setTimeZone( timezone );
197
198         String timestamp = fmt.format( lastUpdatedTimestamp );
199
200         String targetRepoPath = targetRepoConfig.getLocation();
201
202         String sourceRepoPath = sourceRepoConfig.getLocation();
203
204         String artifactPath = pathTranslator.toPath( artifactMetadata.getNamespace(), artifactMetadata.getProject(),
205                                                      artifactMetadata.getProjectVersion(), artifactMetadata.getId() );
206
207         Path sourceArtifactFile = Paths.get( sourceRepoPath, artifactPath );
208
209         Path targetArtifactFile = Paths.get( targetRepoPath, artifactPath );
210
211         log.debug( "artifactPath {}", artifactPath );
212
213         int lastIndex = artifactPath.lastIndexOf( RepositoryPathTranslator.PATH_SEPARATOR );
214
215         Path targetFile = Paths.get( targetRepoPath, artifactPath.substring( 0, lastIndex ) );
216
217         if ( !Files.exists(targetFile) )
218         {
219             // create the folder structure when it does not exist
220             Files.createDirectories(targetFile);
221         }
222         // artifact copying
223         copyFile( sourceArtifactFile, targetArtifactFile );
224
225         // pom file copying
226         // TODO need to use path translator to get the pom file path
227 //        String fileName = artifactMetadata.getProject() + "-" + artifactMetadata.getVersion() + ".pom";
228 //
229 //        File sourcePomFile =
230 //            pathTranslator.toFile( new File( sourceRepoPath ), artifactMetadata.getId(), artifactMetadata.getProject(),
231 //                                   artifactMetadata.getVersion(), fileName );
232 //
233 //        String relativePathToPomFile = sourcePomFile.getAbsolutePath().split( sourceRepoPath )[1];
234 //        File targetPomFile = new File( targetRepoPath, relativePathToPomFile );
235
236         //pom file copying  (file path is taken with out using path translator)
237
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" );
246
247         if ( !Files.exists(targetPomFile) && Files.exists(sourcePomFile) )
248         {
249             copyFile( sourcePomFile, targetPomFile );
250         }
251
252         // explicitly update only if metadata-updater consumer is not enabled!
253         if ( !config.getRepositoryScanning().getKnownContentConsumers().contains( "metadata-updater" ) )
254         {
255
256             // updating version metadata files
257             FilesystemStorage fsStorage = new FilesystemStorage(Paths.get(sourceRepoPath), new DefaultFileLockManager());
258
259             StorageAsset versionMetaDataFileInSourceRepo =
260                 pathTranslator.toFile( new FilesystemAsset(fsStorage, "", Paths.get(sourceRepoPath)), artifactMetadata.getNamespace(),
261                                        artifactMetadata.getProject(), artifactMetadata.getVersion(),
262                                        METADATA_FILENAME );
263
264             if ( versionMetaDataFileInSourceRepo.exists() )
265             {//Pattern quote for windows path
266                 String relativePathToVersionMetadataFile =
267                         getRelativeAssetPath(versionMetaDataFileInSourceRepo);
268                 Path versionMetaDataFileInTargetRepo = Paths.get( targetRepoPath, relativePathToVersionMetadataFile );
269
270                 if ( !Files.exists(versionMetaDataFileInTargetRepo) )
271                 {
272                     copyFile( versionMetaDataFileInSourceRepo.getFilePath(), versionMetaDataFileInTargetRepo );
273                 }
274                 else
275                 {
276                     updateVersionMetadata( versionMetaDataFileInTargetRepo, artifactMetadata, lastUpdatedTimestamp );
277
278                 }
279             }
280
281             // updating project meta data file
282             StorageAsset projectDirectoryInSourceRepo = versionMetaDataFileInSourceRepo.getParent().getParent();
283             StorageAsset projectMetadataFileInSourceRepo = projectDirectoryInSourceRepo.resolve(METADATA_FILENAME );
284
285             if ( projectMetadataFileInSourceRepo.exists() )
286             {
287                 String relativePathToProjectMetadataFile =
288                         getRelativeAssetPath(projectMetadataFileInSourceRepo);
289                 Path projectMetadataFileInTargetRepo = Paths.get( targetRepoPath, relativePathToProjectMetadataFile );
290
291                 if ( !Files.exists(projectMetadataFileInTargetRepo) )
292                 {
293
294                     copyFile( projectMetadataFileInSourceRepo.getFilePath(), projectMetadataFileInTargetRepo );
295                 }
296                 else
297                 {
298                     updateProjectMetadata( projectMetadataFileInTargetRepo, artifactMetadata, lastUpdatedTimestamp,
299                                            timestamp );
300                 }
301             }
302         }
303
304     }
305
306     private String getRelativeAssetPath(final StorageAsset asset) {
307         String relPath = asset.getPath();
308         while(relPath.startsWith("/")) {
309             relPath = relPath.substring(1);
310         }
311         return relPath;
312     }
313
314     private void copyFile( Path sourceFile, Path targetFile )
315         throws IOException
316     {
317
318         FileUtils.copyFile( sourceFile.toFile(), targetFile.toFile() );
319
320     }
321
322     private void updateProjectMetadata( Path projectMetaDataFileIntargetRepo, ArtifactMetadata artifactMetadata,
323                                         Date lastUpdatedTimestamp, String timestamp )
324         throws RepositoryMetadataException
325     {
326         ArrayList<String> availableVersions = new ArrayList<>();
327         String latestVersion = artifactMetadata.getProjectVersion();
328
329         ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetaDataFileIntargetRepo );
330
331         if ( Files.exists(projectMetaDataFileIntargetRepo) )
332         {
333             availableVersions = (ArrayList<String>) projectMetadata.getAvailableVersions();
334
335             Collections.sort( availableVersions, VersionComparator.getInstance() );
336
337             if ( !availableVersions.contains( artifactMetadata.getVersion() ) )
338             {
339                 availableVersions.add( artifactMetadata.getVersion() );
340             }
341
342             latestVersion = availableVersions.get( availableVersions.size() - 1 );
343         }
344         else
345         {
346             availableVersions.add( artifactMetadata.getProjectVersion() );
347             projectMetadata.setGroupId( artifactMetadata.getNamespace() );
348             projectMetadata.setArtifactId( artifactMetadata.getProject() );
349         }
350
351         if ( projectMetadata.getGroupId() == null )
352         {
353             projectMetadata.setGroupId( artifactMetadata.getNamespace() );
354         }
355
356         if ( projectMetadata.getArtifactId() == null )
357         {
358             projectMetadata.setArtifactId( artifactMetadata.getProject() );
359         }
360
361         projectMetadata.setLatestVersion( latestVersion );
362         projectMetadata.setAvailableVersions( availableVersions );
363         projectMetadata.setLastUpdated( timestamp );
364         projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
365
366         if ( !VersionUtil.isSnapshot( artifactMetadata.getVersion() ) )
367         {
368             projectMetadata.setReleasedVersion( latestVersion );
369         }
370
371         try(BufferedWriter writer = Files.newBufferedWriter(projectMetaDataFileIntargetRepo)) {
372             RepositoryMetadataWriter.write( projectMetadata, writer );
373         } catch (IOException e) {
374             throw new RepositoryMetadataException(e);
375         }
376
377     }
378
379     private void updateVersionMetadata( Path versionMetaDataFileInTargetRepo, ArtifactMetadata artifactMetadata,
380                                         Date lastUpdatedTimestamp )
381         throws RepositoryMetadataException
382     {
383         ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetaDataFileInTargetRepo );
384         if ( !Files.exists(versionMetaDataFileInTargetRepo) )
385         {
386             versionMetadata.setGroupId( artifactMetadata.getNamespace() );
387             versionMetadata.setArtifactId( artifactMetadata.getProject() );
388             versionMetadata.setVersion( artifactMetadata.getProjectVersion() );
389         }
390
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);
396         }
397     }
398
399     private ArchivaRepositoryMetadata getMetadata( Path metadataFile )
400         throws RepositoryMetadataException
401     {
402         ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
403         if ( Files.exists(metadataFile) )
404         {
405             metadata = metadataReader.read( metadataFile );
406         }
407         return metadata;
408     }
409
410     @Override
411     public List<ArtifactMetadata> getConflictingArtifacts( MetadataRepository metadataRepository, String sourceRepo,
412                                                            String targetRepo )
413         throws RepositoryMergerException
414     {
415         try(RepositorySession session = repositorySessionFactory.createSession())
416         {
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);
422
423             return new ArrayList<>(sourceArtifacts);
424         }
425         catch ( MetadataRepositoryException e )
426         {
427             throw new RepositoryMergerException( e.getMessage(), e );
428         }
429     }
430
431     public RepositorySessionFactory getRepositorySessionFactory( )
432     {
433         return repositorySessionFactory;
434     }
435
436     public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory )
437     {
438         this.repositorySessionFactory = repositorySessionFactory;
439     }
440 }