This project has retired. For details please refer to its Attic page.
Source code
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.filelock.DefaultFileLockManager;
023import org.apache.archiva.common.utils.VersionComparator;
024import org.apache.archiva.common.utils.VersionUtil;
025import org.apache.archiva.configuration.ArchivaConfiguration;
026import org.apache.archiva.configuration.Configuration;
027import org.apache.archiva.configuration.ManagedRepositoryConfiguration;
028import org.apache.archiva.maven2.metadata.MavenMetadataReader;
029import org.apache.archiva.metadata.model.ArtifactMetadata;
030import org.apache.archiva.metadata.repository.MetadataRepository;
031import org.apache.archiva.metadata.repository.MetadataRepositoryException;
032import org.apache.archiva.filter.Filter;
033import org.apache.archiva.metadata.repository.RepositorySession;
034import org.apache.archiva.metadata.repository.RepositorySessionFactory;
035import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
036import org.apache.archiva.model.ArchivaRepositoryMetadata;
037import org.apache.archiva.repository.RepositoryException;
038import org.apache.archiva.repository.RepositoryType;
039import org.apache.archiva.repository.metadata.RepositoryMetadataException;
040import org.apache.archiva.repository.metadata.base.RepositoryMetadataWriter;
041import org.apache.archiva.repository.storage.FilesystemAsset;
042import org.apache.archiva.repository.storage.FilesystemStorage;
043import org.apache.archiva.repository.storage.StorageAsset;
044import org.apache.archiva.xml.XMLException;
045import org.apache.commons.io.FileUtils;
046import org.slf4j.Logger;
047import org.slf4j.LoggerFactory;
048import org.springframework.stereotype.Service;
049
050import javax.inject.Inject;
051import javax.inject.Named;
052import java.io.BufferedWriter;
053import java.io.IOException;
054import java.nio.file.Files;
055import java.nio.file.Path;
056import java.nio.file.Paths;
057import java.text.DateFormat;
058import java.text.SimpleDateFormat;
059import java.util.*;
060
061/**
062 *
063 */
064@Service ("repositoryMerger#maven2")
065public class Maven2RepositoryMerger
066    implements RepositoryMerger
067{
068
069    private Logger log = LoggerFactory.getLogger( getClass() );
070
071    private static final Comparator<ArtifactMetadata> META_COMPARATOR = Comparator.comparing(ArtifactMetadata::getNamespace)
072            .thenComparing(ArtifactMetadata::getProject)
073            .thenComparing(ArtifactMetadata::getId)
074            .thenComparing(ArtifactMetadata::getVersion);
075
076    /**
077     *
078     */
079    private ArchivaConfiguration configuration;
080
081    /**
082     *
083     */
084    private RepositoryPathTranslator pathTranslator;
085
086    private static final String METADATA_FILENAME = "maven-metadata.xml";
087
088    @Inject
089    private RepositorySessionFactory repositorySessionFactory;
090
091    @Inject
092    public Maven2RepositoryMerger(
093        @Named (value = "archivaConfiguration#default") ArchivaConfiguration archivaConfiguration,
094        @Named (value = "repositoryPathTranslator#maven2") RepositoryPathTranslator repositoryPathTranslator )
095    {
096        this.configuration = archivaConfiguration;
097        this.pathTranslator = repositoryPathTranslator;
098    }
099
100    public void setConfiguration( ArchivaConfiguration configuration )
101    {
102        this.configuration = configuration;
103    }
104
105    @Override
106    public boolean supportsRepository( RepositoryType type )
107    {
108        return RepositoryType.MAVEN.equals( type );
109    }
110
111    @Override
112    public void merge( MetadataRepository metadataRepository, String sourceRepoId, String targetRepoId )
113        throws RepositoryMergerException
114    {
115
116        try(RepositorySession session = repositorySessionFactory.createSession())
117        {
118            List<ArtifactMetadata> artifactsInSourceRepo = metadataRepository.getArtifacts(session , sourceRepoId );
119            for ( ArtifactMetadata artifactMetadata : artifactsInSourceRepo )
120            {
121                artifactMetadata.setRepositoryId( targetRepoId );
122                createFolderStructure( sourceRepoId, targetRepoId, artifactMetadata );
123            }
124        }
125        catch ( MetadataRepositoryException e )
126        {
127            throw new RepositoryMergerException( e.getMessage(), e );
128        }
129        catch ( IOException e )
130        {
131            throw new RepositoryMergerException( e.getMessage(), e );
132        }
133        catch ( RepositoryException e )
134        {
135            throw new RepositoryMergerException( e.getMessage(), e );
136        }
137    }
138
139    // TODO when UI needs a subset to merge
140    @Override
141    public void merge( MetadataRepository metadataRepository, String sourceRepoId, String targetRepoId,
142                       Filter<ArtifactMetadata> filter )
143        throws RepositoryMergerException
144    {
145        try(RepositorySession session = repositorySessionFactory.createSession())
146        {
147            List<ArtifactMetadata> sourceArtifacts = metadataRepository.getArtifacts(session , sourceRepoId );
148            for ( ArtifactMetadata metadata : sourceArtifacts )
149            {
150                if ( filter.accept( metadata ) )
151                {
152                    createFolderStructure( sourceRepoId, targetRepoId, metadata );
153                }
154            }
155        }
156        catch ( MetadataRepositoryException e )
157        {
158            throw new RepositoryMergerException( e.getMessage(), e );
159        }
160        catch ( IOException e )
161        {
162            throw new RepositoryMergerException( e.getMessage(), e );
163        }
164        catch ( RepositoryException e )
165        {
166            throw new RepositoryMergerException( e.getMessage(), e );
167        }
168    }
169
170    private void createFolderStructure( String sourceRepoId, String targetRepoId, ArtifactMetadata artifactMetadata )
171        throws IOException, RepositoryException
172    {
173        Configuration config = configuration.getConfiguration();
174
175        ManagedRepositoryConfiguration targetRepoConfig = config.findManagedRepositoryById( targetRepoId );
176
177        ManagedRepositoryConfiguration sourceRepoConfig = config.findManagedRepositoryById( sourceRepoId );
178
179        Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
180
181        TimeZone timezone = TimeZone.getTimeZone( "UTC" );
182
183        DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
184
185        fmt.setTimeZone( timezone );
186
187        String timestamp = fmt.format( lastUpdatedTimestamp );
188
189        String targetRepoPath = targetRepoConfig.getLocation();
190
191        String sourceRepoPath = sourceRepoConfig.getLocation();
192
193        String artifactPath = pathTranslator.toPath( artifactMetadata.getNamespace(), artifactMetadata.getProject(),
194                                                     artifactMetadata.getProjectVersion(), artifactMetadata.getId() );
195
196        Path sourceArtifactFile = Paths.get( sourceRepoPath, artifactPath );
197
198        Path targetArtifactFile = Paths.get( targetRepoPath, artifactPath );
199
200        log.debug( "artifactPath {}", artifactPath );
201
202        int lastIndex = artifactPath.lastIndexOf( RepositoryPathTranslator.PATH_SEPARATOR );
203
204        Path targetFile = Paths.get( targetRepoPath, artifactPath.substring( 0, lastIndex ) );
205
206        if ( !Files.exists(targetFile) )
207        {
208            // create the folder structure when it does not exist
209            Files.createDirectories(targetFile);
210        }
211        // artifact copying
212        copyFile( sourceArtifactFile, targetArtifactFile );
213
214        // pom file copying
215        // TODO need to use path translator to get the pom file path
216//        String fileName = artifactMetadata.getProject() + "-" + artifactMetadata.getVersion() + ".pom";
217//
218//        File sourcePomFile =
219//            pathTranslator.toFile( new File( sourceRepoPath ), artifactMetadata.getId(), artifactMetadata.getProject(),
220//                                   artifactMetadata.getVersion(), fileName );
221//
222//        String relativePathToPomFile = sourcePomFile.getAbsolutePath().split( sourceRepoPath )[1];
223//        File targetPomFile = new File( targetRepoPath, relativePathToPomFile );
224
225        //pom file copying  (file path is taken with out using path translator)
226
227        String index = artifactPath.substring( lastIndex + 1 );
228        int last = index.lastIndexOf( '.' );
229        Path sourcePomFile = Paths.get( sourceRepoPath,
230                                       artifactPath.substring( 0, lastIndex ) + "/" + artifactPath.substring(
231                                           lastIndex + 1 ).substring( 0, last ) + ".pom" );
232        Path targetPomFile = Paths.get( targetRepoPath,
233                                       artifactPath.substring( 0, lastIndex ) + "/" + artifactPath.substring(
234                                           lastIndex + 1 ).substring( 0, last ) + ".pom" );
235
236        if ( !Files.exists(targetPomFile) && Files.exists(sourcePomFile) )
237        {
238            copyFile( sourcePomFile, targetPomFile );
239        }
240
241        // explicitly update only if metadata-updater consumer is not enabled!
242        if ( !config.getRepositoryScanning().getKnownContentConsumers().contains( "metadata-updater" ) )
243        {
244
245            // updating version metadata files
246            FilesystemStorage fsStorage = new FilesystemStorage(Paths.get(sourceRepoPath), new DefaultFileLockManager());
247
248            StorageAsset versionMetaDataFileInSourceRepo =
249                pathTranslator.toFile( new FilesystemAsset(fsStorage, "", Paths.get(sourceRepoPath)), artifactMetadata.getNamespace(),
250                                       artifactMetadata.getProject(), artifactMetadata.getVersion(),
251                                       METADATA_FILENAME );
252
253            if ( versionMetaDataFileInSourceRepo.exists() )
254            {//Pattern quote for windows path
255                String relativePathToVersionMetadataFile =
256                        getRelativeAssetPath(versionMetaDataFileInSourceRepo);
257                Path versionMetaDataFileInTargetRepo = Paths.get( targetRepoPath, relativePathToVersionMetadataFile );
258
259                if ( !Files.exists(versionMetaDataFileInTargetRepo) )
260                {
261                    copyFile( versionMetaDataFileInSourceRepo.getFilePath(), versionMetaDataFileInTargetRepo );
262                }
263                else
264                {
265                    updateVersionMetadata( versionMetaDataFileInTargetRepo, artifactMetadata, lastUpdatedTimestamp );
266
267                }
268            }
269
270            // updating project meta data file
271            StorageAsset projectDirectoryInSourceRepo = versionMetaDataFileInSourceRepo.getParent().getParent();
272            StorageAsset projectMetadataFileInSourceRepo = projectDirectoryInSourceRepo.resolve(METADATA_FILENAME );
273
274            if ( projectMetadataFileInSourceRepo.exists() )
275            {
276                String relativePathToProjectMetadataFile =
277                        getRelativeAssetPath(projectMetadataFileInSourceRepo);
278                Path projectMetadataFileInTargetRepo = Paths.get( targetRepoPath, relativePathToProjectMetadataFile );
279
280                if ( !Files.exists(projectMetadataFileInTargetRepo) )
281                {
282
283                    copyFile( projectMetadataFileInSourceRepo.getFilePath(), projectMetadataFileInTargetRepo );
284                }
285                else
286                {
287                    updateProjectMetadata( projectMetadataFileInTargetRepo, artifactMetadata, lastUpdatedTimestamp,
288                                           timestamp );
289                }
290            }
291        }
292
293    }
294
295    private String getRelativeAssetPath(final StorageAsset asset) {
296        String relPath = asset.getPath();
297        while(relPath.startsWith("/")) {
298            relPath = relPath.substring(1);
299        }
300        return relPath;
301    }
302
303    private void copyFile( Path sourceFile, Path targetFile )
304        throws IOException
305    {
306
307        FileUtils.copyFile( sourceFile.toFile(), targetFile.toFile() );
308
309    }
310
311    private void updateProjectMetadata( Path projectMetaDataFileIntargetRepo, ArtifactMetadata artifactMetadata,
312                                        Date lastUpdatedTimestamp, String timestamp )
313        throws RepositoryMetadataException
314    {
315        ArrayList<String> availableVersions = new ArrayList<>();
316        String latestVersion = artifactMetadata.getProjectVersion();
317
318        ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetaDataFileIntargetRepo );
319
320        if ( Files.exists(projectMetaDataFileIntargetRepo) )
321        {
322            availableVersions = (ArrayList<String>) projectMetadata.getAvailableVersions();
323
324            Collections.sort( availableVersions, VersionComparator.getInstance() );
325
326            if ( !availableVersions.contains( artifactMetadata.getVersion() ) )
327            {
328                availableVersions.add( artifactMetadata.getVersion() );
329            }
330
331            latestVersion = availableVersions.get( availableVersions.size() - 1 );
332        }
333        else
334        {
335            availableVersions.add( artifactMetadata.getProjectVersion() );
336            projectMetadata.setGroupId( artifactMetadata.getNamespace() );
337            projectMetadata.setArtifactId( artifactMetadata.getProject() );
338        }
339
340        if ( projectMetadata.getGroupId() == null )
341        {
342            projectMetadata.setGroupId( artifactMetadata.getNamespace() );
343        }
344
345        if ( projectMetadata.getArtifactId() == null )
346        {
347            projectMetadata.setArtifactId( artifactMetadata.getProject() );
348        }
349
350        projectMetadata.setLatestVersion( latestVersion );
351        projectMetadata.setAvailableVersions( availableVersions );
352        projectMetadata.setLastUpdated( timestamp );
353        projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
354
355        if ( !VersionUtil.isSnapshot( artifactMetadata.getVersion() ) )
356        {
357            projectMetadata.setReleasedVersion( latestVersion );
358        }
359
360        try(BufferedWriter writer = Files.newBufferedWriter(projectMetaDataFileIntargetRepo)) {
361            RepositoryMetadataWriter.write( projectMetadata, writer );
362        } catch (IOException e) {
363            throw new RepositoryMetadataException(e);
364        }
365
366    }
367
368    private void updateVersionMetadata( Path versionMetaDataFileInTargetRepo, ArtifactMetadata artifactMetadata,
369                                        Date lastUpdatedTimestamp )
370        throws RepositoryMetadataException
371    {
372        ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetaDataFileInTargetRepo );
373        if ( !Files.exists(versionMetaDataFileInTargetRepo) )
374        {
375            versionMetadata.setGroupId( artifactMetadata.getNamespace() );
376            versionMetadata.setArtifactId( artifactMetadata.getProject() );
377            versionMetadata.setVersion( artifactMetadata.getProjectVersion() );
378        }
379
380        versionMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
381        try(BufferedWriter writer = Files.newBufferedWriter(versionMetaDataFileInTargetRepo) ) {
382            RepositoryMetadataWriter.write( versionMetadata, writer);
383        } catch (IOException e) {
384            throw new RepositoryMetadataException(e);
385        }
386    }
387
388    private ArchivaRepositoryMetadata getMetadata( Path metadataFile )
389        throws RepositoryMetadataException
390    {
391        ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
392        if ( Files.exists(metadataFile) )
393        {
394            try
395            {
396                metadata = MavenMetadataReader.read( metadataFile );
397            }
398            catch (XMLException e )
399            {
400                throw new RepositoryMetadataException( e.getMessage(), e );
401            }
402        }
403        return metadata;
404    }
405
406    @Override
407    public List<ArtifactMetadata> getConflictingArtifacts( MetadataRepository metadataRepository, String sourceRepo,
408                                                           String targetRepo )
409        throws RepositoryMergerException
410    {
411        try(RepositorySession session = repositorySessionFactory.createSession())
412        {
413            TreeSet<ArtifactMetadata> targetArtifacts = new TreeSet<>(META_COMPARATOR);
414            targetArtifacts.addAll(metadataRepository.getArtifacts(session , targetRepo ));
415            TreeSet<ArtifactMetadata> sourceArtifacts = new TreeSet<>(META_COMPARATOR);
416            sourceArtifacts.addAll(metadataRepository.getArtifacts(session , sourceRepo ));
417            sourceArtifacts.retainAll(targetArtifacts);
418
419            return new ArrayList<>(sourceArtifacts);
420        }
421        catch ( MetadataRepositoryException e )
422        {
423            throw new RepositoryMergerException( e.getMessage(), e );
424        }
425    }
426
427    public RepositorySessionFactory getRepositorySessionFactory( )
428    {
429        return repositorySessionFactory;
430    }
431
432    public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory )
433    {
434        this.repositorySessionFactory = repositorySessionFactory;
435    }
436}