This project has retired. For details please refer to its Attic page.
Source code
001package org.apache.archiva.web.api;
002/*
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 */
020
021import com.google.common.base.Predicate;
022import com.google.common.collect.Iterables;
023import org.apache.archiva.admin.model.RepositoryAdminException;
024import org.apache.archiva.admin.model.admin.ArchivaAdministration;
025import org.apache.archiva.admin.model.beans.ManagedRepository;
026import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
027import org.apache.archiva.metadata.model.facets.AuditEvent;
028import org.apache.archiva.checksum.ChecksumAlgorithm;
029import org.apache.archiva.checksum.ChecksummedFile;
030import org.apache.archiva.common.utils.VersionComparator;
031import org.apache.archiva.common.utils.VersionUtil;
032import org.apache.archiva.maven2.metadata.MavenMetadataReader;
033import org.apache.archiva.model.ArchivaRepositoryMetadata;
034import org.apache.archiva.model.ArtifactReference;
035import org.apache.archiva.model.SnapshotVersion;
036import org.apache.archiva.redback.components.taskqueue.TaskQueueException;
037import org.apache.archiva.repository.ManagedRepositoryContent;
038import org.apache.archiva.repository.RepositoryContentFactory;
039import org.apache.archiva.repository.RepositoryException;
040import org.apache.archiva.repository.RepositoryNotFoundException;
041import org.apache.archiva.repository.metadata.MetadataTools;
042import org.apache.archiva.repository.metadata.RepositoryMetadataException;
043import org.apache.archiva.repository.metadata.RepositoryMetadataWriter;
044import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
045import org.apache.archiva.rest.services.AbstractRestService;
046import org.apache.archiva.scheduler.ArchivaTaskScheduler;
047import org.apache.archiva.scheduler.repository.model.RepositoryTask;
048import org.apache.archiva.web.model.FileMetadata;
049import org.apache.archiva.xml.XMLException;
050import org.apache.commons.io.FilenameUtils;
051import org.apache.commons.io.IOUtils;
052import org.apache.commons.lang.BooleanUtils;
053import org.apache.commons.lang.StringEscapeUtils;
054import org.apache.commons.lang.StringUtils;
055import org.apache.commons.lang.SystemUtils;
056import org.apache.cxf.jaxrs.ext.multipart.Attachment;
057import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
058import org.apache.maven.model.Model;
059import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
060import org.slf4j.Logger;
061import org.slf4j.LoggerFactory;
062import org.springframework.stereotype.Service;
063
064import javax.inject.Inject;
065import javax.inject.Named;
066import javax.servlet.http.HttpServletRequest;
067import javax.ws.rs.core.Context;
068import javax.ws.rs.core.Response;
069import java.io.File;
070import java.io.FileOutputStream;
071import java.io.FileWriter;
072import java.io.IOException;
073import java.net.URLDecoder;
074import java.nio.file.*;
075import java.text.DateFormat;
076import java.text.SimpleDateFormat;
077import java.util.ArrayList;
078import java.util.Calendar;
079import java.util.Collections;
080import java.util.Date;
081import java.util.Iterator;
082import java.util.List;
083import java.util.TimeZone;
084import java.util.concurrent.CopyOnWriteArrayList;
085
086/**
087 * @author Olivier Lamy
088 */
089@Service("fileUploadService#rest")
090public class DefaultFileUploadService
091    extends AbstractRestService
092    implements FileUploadService
093{
094    private Logger log = LoggerFactory.getLogger( getClass() );
095
096    @Context
097    private HttpServletRequest httpServletRequest;
098
099    @Inject
100    private ManagedRepositoryAdmin managedRepositoryAdmin;
101
102    @Inject
103    private RepositoryContentFactory repositoryFactory;
104
105    @Inject
106    private ArchivaAdministration archivaAdministration;
107
108    private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
109
110    private final String FS = FileSystems.getDefault().getSeparator();
111
112    @Inject
113    @Named(value = "archivaTaskScheduler#repository")
114    private ArchivaTaskScheduler scheduler;
115
116    private String getStringValue( MultipartBody multipartBody, String attachmentId )
117        throws IOException
118    {
119        Attachment attachment = multipartBody.getAttachment( attachmentId );
120        return attachment == null ? "" :
121            StringUtils.trim(URLDecoder.decode(IOUtils.toString( attachment.getDataHandler().getInputStream() ), "UTF-8"));
122    }
123
124    @Override
125    public FileMetadata post( MultipartBody multipartBody )
126        throws ArchivaRestServiceException
127    {
128
129        try
130        {
131
132            String classifier = getStringValue( multipartBody, "classifier" );
133            String packaging = getStringValue( multipartBody, "packaging" );
134
135            checkParamChars( "classifier", classifier );
136            checkParamChars( "packaging", packaging);
137
138            // skygo: http header form pomFile was once sending 1 for true and void for false
139            // leading to permanent false value for pomFile if using toBoolean(); use , "1", ""
140
141            boolean pomFile = false;
142            try
143            {
144                pomFile = BooleanUtils.toBoolean( getStringValue( multipartBody, "pomFile" ) );
145            }
146            catch ( IllegalArgumentException ex )
147            {
148                ArchivaRestServiceException e = new ArchivaRestServiceException("Bad value for boolean pomFile field.", null);
149                e.setHttpErrorCode(422);
150                e.setFieldName( "pomFile" );
151                e.setErrorKey("fileupload.malformed.pomFile");
152                throw e;
153            }
154
155            Attachment file = multipartBody.getAttachment( "files[]" );
156
157            //Content-Disposition: form-data; name="files[]"; filename="org.apache.karaf.features.command-2.2.2.jar"
158            String fileName = file.getContentDisposition().getParameter( "filename" );
159            Path fileNamePath = Paths.get(fileName);
160            if (!fileName.equals(fileNamePath.getFileName().toString())) {
161                ArchivaRestServiceException e = new ArchivaRestServiceException("Bad filename in upload content: " + fileName + " - File traversal chars (..|/) are not allowed"
162                        , null);
163                e.setHttpErrorCode(422);
164                e.setErrorKey("fileupload.malformed.filename");
165                throw e;
166            }
167
168            File tmpFile = File.createTempFile( "upload-artifact", ".tmp" );
169            tmpFile.deleteOnExit();
170            IOUtils.copy( file.getDataHandler().getInputStream(), new FileOutputStream( tmpFile ) );
171            FileMetadata fileMetadata = new FileMetadata( fileName, tmpFile.length(), "theurl" );
172            fileMetadata.setServerFileName( tmpFile.getPath() );
173            fileMetadata.setClassifier( classifier );
174            fileMetadata.setDeleteUrl( tmpFile.getName() );
175            fileMetadata.setPomFile( pomFile );
176            fileMetadata.setPackaging( packaging );
177
178            log.info( "uploading file: {}", fileMetadata );
179
180            List<FileMetadata> fileMetadatas = getSessionFilesList();
181
182            fileMetadatas.add( fileMetadata );
183
184            return fileMetadata;
185        }
186        catch ( IOException e )
187        {
188            throw new ArchivaRestServiceException( e.getMessage(),
189                                                   Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
190        }
191
192    }
193
194    /**
195     * FIXME must be per session synchronized not globally
196     *
197     * @return
198     */
199    protected synchronized List<FileMetadata> getSessionFilesList()
200    {
201        List<FileMetadata> fileMetadatas =
202            (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
203        if ( fileMetadatas == null )
204        {
205            fileMetadatas = new CopyOnWriteArrayList<>();
206            httpServletRequest.getSession().setAttribute( FILES_SESSION_KEY, fileMetadatas );
207        }
208        return fileMetadatas;
209    }
210
211    @Override
212    public Boolean deleteFile( String fileName )
213        throws ArchivaRestServiceException
214    {
215        // we make sure, that there are no other path components in the filename:
216        String checkedFileName = Paths.get(fileName).getFileName().toString();
217        File file = new File( SystemUtils.getJavaIoTmpDir(), checkedFileName );
218        log.debug( "delete file:{},exists:{}", file.getPath(), file.exists() );
219        boolean removed = getSessionFileMetadatas().remove( new FileMetadata( fileName ) );
220        // try with full name as ui only know the file name
221        if ( !removed )
222        {
223            removed = getSessionFileMetadatas().remove( new FileMetadata( file.getPath() ) );
224        }
225        if (removed && file.exists() )
226        {
227            return file.delete();
228        }
229        return Boolean.FALSE;
230    }
231
232    @Override
233    public Boolean clearUploadedFiles()
234        throws ArchivaRestServiceException
235    {
236        List<FileMetadata> fileMetadatas = new ArrayList( getSessionFileMetadatas() );
237        for ( FileMetadata fileMetadata : fileMetadatas )
238        {
239            deleteFile( new File( fileMetadata.getServerFileName() ).getPath() );
240        }
241        getSessionFileMetadatas().clear();
242        return Boolean.TRUE;
243    }
244
245    @Override
246    public List<FileMetadata> getSessionFileMetadatas()
247        throws ArchivaRestServiceException
248    {
249        List<FileMetadata> fileMetadatas =
250            (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
251
252        return fileMetadatas == null ? Collections.<FileMetadata>emptyList() : fileMetadatas;
253    }
254
255    private boolean hasValidChars(String checkString) {
256        if (checkString.contains(FS)) {
257            return false;
258        }
259        if (checkString.contains("../")) {
260            return false;
261        }
262        if (checkString.contains("/..")) {
263            return false;
264        }
265        if (checkString.contains("<")) {
266            return false;
267        }
268        if (checkString.contains(">")) {
269            return false;
270        }
271        if (checkString.contains("&")) {
272            return false;
273        }
274        return true;
275    }
276
277    private void checkParamChars(String param, String value) throws ArchivaRestServiceException {
278        if (!hasValidChars(value)) {
279            ArchivaRestServiceException e = new ArchivaRestServiceException("Bad characters in " + param, null);
280            e.setHttpErrorCode(422);
281            e.setErrorKey("fileupload.malformed.param");
282            e.setFieldName(param);
283            throw e;
284        }
285    }
286
287    @Override
288    public Boolean save( String repositoryId, String groupId, String artifactId, String version, String packaging,
289                         boolean generatePom )
290        throws ArchivaRestServiceException
291    {
292        repositoryId = StringEscapeUtils.escapeHtml( StringUtils.trim( repositoryId ) );
293        groupId = StringEscapeUtils.escapeHtml( StringUtils.trim( groupId ) );
294        artifactId = StringEscapeUtils.escapeHtml( StringUtils.trim( artifactId ) );
295        version = StringEscapeUtils.escapeHtml( StringUtils.trim( version ) );
296        packaging = StringEscapeUtils.escapeHtml( StringUtils.trim( packaging ) );
297
298        checkParamChars("repositoryId", repositoryId);
299        checkParamChars("groupId", groupId);
300        checkParamChars("artifactId", artifactId);
301        checkParamChars( "version", version);
302        checkParamChars("packaging", packaging);
303
304
305        List<FileMetadata> fileMetadatas = getSessionFilesList();
306        if ( fileMetadatas == null || fileMetadatas.isEmpty() )
307        {
308            return Boolean.FALSE;
309        }
310
311        try
312        {
313            ManagedRepository managedRepository = managedRepositoryAdmin.getManagedRepository( repositoryId );
314
315            if ( managedRepository == null )
316            {
317                // TODO i18n ?
318                throw new ArchivaRestServiceException( "Cannot find managed repository with id " + repositoryId,
319                                                       Response.Status.BAD_REQUEST.getStatusCode(), null );
320            }
321
322            if ( VersionUtil.isSnapshot( version ) && !managedRepository.isSnapshots() )
323            {
324                // TODO i18n ?
325                throw new ArchivaRestServiceException(
326                    "Managed repository with id " + repositoryId + " do not accept snapshots",
327                    Response.Status.BAD_REQUEST.getStatusCode(), null );
328            }
329        }
330        catch ( RepositoryAdminException e )
331        {
332            throw new ArchivaRestServiceException( e.getMessage(),
333                                                   Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
334        }
335
336        // get from the session file with groupId/artifactId
337
338        Iterable<FileMetadata> filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
339        {
340            public boolean apply( FileMetadata fileMetadata )
341            {
342                return fileMetadata != null && !fileMetadata.isPomFile();
343            }
344        } );
345        Iterator<FileMetadata> iterator = filesToAdd.iterator();
346        boolean pomGenerated = false;
347        while ( iterator.hasNext() )
348        {
349            FileMetadata fileMetadata = iterator.next();
350            log.debug( "fileToAdd: {}", fileMetadata );
351            saveFile( repositoryId, fileMetadata, generatePom && !pomGenerated, groupId, artifactId, version,
352                      packaging );
353            pomGenerated = true;
354            deleteFile( fileMetadata.getServerFileName() );
355        }
356
357        filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
358        {
359            @Override
360            public boolean apply( FileMetadata fileMetadata )
361            {
362                return fileMetadata != null && fileMetadata.isPomFile();
363            }
364        } );
365
366        iterator = filesToAdd.iterator();
367        while ( iterator.hasNext() )
368        {
369            FileMetadata fileMetadata = iterator.next();
370            log.debug( "fileToAdd: {}", fileMetadata );
371            savePomFile( repositoryId, fileMetadata, groupId, artifactId, version, packaging );
372            deleteFile( fileMetadata.getServerFileName() );
373        }
374
375        return Boolean.TRUE;
376    }
377
378    protected void savePomFile( String repositoryId, FileMetadata fileMetadata, String groupId, String artifactId,
379                                String version, String packaging )
380        throws ArchivaRestServiceException
381    {
382
383        try
384        {
385            boolean fixChecksums =
386                !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
387
388            ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
389
390            repositoryId = StringEscapeUtils.escapeHtml( StringUtils.trim( repositoryId ) );
391            groupId = StringEscapeUtils.escapeHtml( StringUtils.trim( groupId ) );
392            artifactId = StringEscapeUtils.escapeHtml( StringUtils.trim( artifactId ) );
393            version = StringEscapeUtils.escapeHtml( StringUtils.trim( version ) );
394            packaging = StringEscapeUtils.escapeHtml( StringUtils.trim( packaging ) );
395
396            ArtifactReference artifactReference = new ArtifactReference();
397            artifactReference.setArtifactId( artifactId );
398            artifactReference.setGroupId( groupId );
399            artifactReference.setVersion( version );
400            artifactReference.setClassifier( fileMetadata.getClassifier() );
401            artifactReference.setType( packaging );
402
403            ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
404
405            String artifactPath = repository.toPath( artifactReference );
406
407            int lastIndex = artifactPath.lastIndexOf( '/' );
408
409            String path = artifactPath.substring( 0, lastIndex );
410            File targetPath = new File( repoConfig.getLocation(), path );
411
412            String pomFilename = artifactPath.substring( lastIndex + 1 );
413            if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
414            {
415                pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
416            }
417            pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
418
419            copyFile( new File( fileMetadata.getServerFileName() ), targetPath, pomFilename, fixChecksums );
420            triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
421            queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
422        }
423        catch ( IOException ie )
424        {
425            throw new ArchivaRestServiceException( "Error encountered while uploading pom file: " + ie.getMessage(),
426                                                   Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie );
427        }
428        catch ( RepositoryException rep )
429        {
430            throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
431                                                   Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rep );
432        }
433        catch ( RepositoryAdminException e )
434        {
435            throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
436                                                   Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
437        }
438    }
439
440    protected void saveFile( String repositoryId, FileMetadata fileMetadata, boolean generatePom, String groupId,
441                             String artifactId, String version, String packaging )
442        throws ArchivaRestServiceException
443    {
444        try
445        {
446
447            ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
448
449            ArtifactReference artifactReference = new ArtifactReference();
450            artifactReference.setArtifactId( artifactId );
451            artifactReference.setGroupId( groupId );
452            artifactReference.setVersion( version );
453            artifactReference.setClassifier( fileMetadata.getClassifier() );
454            artifactReference.setType(
455                StringUtils.isEmpty( fileMetadata.getPackaging() ) ? packaging : fileMetadata.getPackaging() );
456
457            ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
458
459            String artifactPath = repository.toPath( artifactReference );
460
461            int lastIndex = artifactPath.lastIndexOf( '/' );
462
463            String path = artifactPath.substring( 0, lastIndex );
464            File targetPath = new File( repoConfig.getLocation(), path );
465
466            log.debug( "artifactPath: {} found targetPath: {}", artifactPath, targetPath );
467
468            Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
469            int newBuildNumber = -1;
470            String timestamp = null;
471
472            File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA );
473            ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
474
475            if ( VersionUtil.isSnapshot( version ) )
476            {
477                TimeZone timezone = TimeZone.getTimeZone( "UTC" );
478                DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
479                fmt.setTimeZone( timezone );
480                timestamp = fmt.format( lastUpdatedTimestamp );
481                if ( versionMetadata.getSnapshotVersion() != null )
482                {
483                    newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
484                }
485                else
486                {
487                    newBuildNumber = 1;
488                }
489            }
490
491            if ( !targetPath.exists() )
492            {
493                targetPath.mkdirs();
494            }
495
496            String filename = artifactPath.substring( lastIndex + 1 );
497            if ( VersionUtil.isSnapshot( version ) )
498            {
499                filename = filename.replaceAll( VersionUtil.SNAPSHOT, timestamp + "-" + newBuildNumber );
500            }
501
502            boolean fixChecksums =
503                !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
504
505            try
506            {
507                File targetFile = new File( targetPath, filename );
508                if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
509                {
510                    throw new ArchivaRestServiceException(
511                        "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
512                        Response.Status.BAD_REQUEST.getStatusCode(), null );
513                }
514                else
515                {
516                    copyFile( new File( fileMetadata.getServerFileName() ), targetPath, filename, fixChecksums );
517                    triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
518                    queueRepositoryTask( repository.getId(), targetFile );
519                }
520            }
521            catch ( IOException ie )
522            {
523                log.error( "IOException copying file: {}", ie.getMessage(), ie );
524                throw new ArchivaRestServiceException(
525                    "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
526                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie );
527            }
528
529            if ( generatePom )
530            {
531                String pomFilename = filename;
532                if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
533                {
534                    pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
535                }
536                pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
537
538                try
539                {
540                    File generatedPomFile =
541                        createPom( targetPath, pomFilename, fileMetadata, groupId, artifactId, version, packaging );
542                    triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
543                    if ( fixChecksums )
544                    {
545                        fixChecksums( generatedPomFile );
546                    }
547                    queueRepositoryTask( repoConfig.getId(), generatedPomFile );
548                }
549                catch ( IOException ie )
550                {
551                    throw new ArchivaRestServiceException(
552                        "Error encountered while writing pom file: " + ie.getMessage(),
553                        Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie );
554                }
555            }
556
557            // explicitly update only if metadata-updater consumer is not enabled!
558            if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) )
559            {
560                updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
561                                       fixChecksums, fileMetadata, groupId, artifactId, version, packaging );
562
563                if ( VersionUtil.isSnapshot( version ) )
564                {
565                    updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
566                                           newBuildNumber, fixChecksums, fileMetadata, groupId, artifactId, version,
567                                           packaging );
568                }
569            }
570        }
571        catch ( RepositoryNotFoundException re )
572        {
573            throw new ArchivaRestServiceException( "Target repository cannot be found: " + re.getMessage(),
574                                                   Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), re );
575        }
576        catch ( RepositoryException rep )
577        {
578            throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
579                                                   Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rep );
580        }
581        catch ( RepositoryAdminException e )
582        {
583            throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
584                                                   Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
585        }
586    }
587
588    private ArchivaRepositoryMetadata getMetadata( File metadataFile )
589        throws RepositoryMetadataException
590    {
591        ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
592        if ( metadataFile.exists() )
593        {
594            try
595            {
596                metadata = MavenMetadataReader.read( metadataFile );
597            }
598            catch ( XMLException e )
599            {
600                throw new RepositoryMetadataException( e.getMessage(), e );
601            }
602        }
603        return metadata;
604    }
605
606    private File createPom( File targetPath, String filename, FileMetadata fileMetadata, String groupId,
607                            String artifactId, String version, String packaging )
608        throws IOException
609    {
610        Model projectModel = new Model();
611        projectModel.setModelVersion( "4.0.0" );
612        projectModel.setGroupId( groupId );
613        projectModel.setArtifactId( artifactId );
614        projectModel.setVersion( version );
615        projectModel.setPackaging( packaging );
616
617        File pomFile = new File( targetPath, filename );
618        MavenXpp3Writer writer = new MavenXpp3Writer();
619
620        try (FileWriter w = new FileWriter( pomFile ))
621        {
622            writer.write( w, projectModel );
623        }
624
625        return pomFile;
626    }
627
628    private void fixChecksums( File file )
629    {
630        ChecksummedFile checksum = new ChecksummedFile( file );
631        checksum.fixChecksums( algorithms );
632    }
633
634    private void queueRepositoryTask( String repositoryId, File localFile )
635    {
636        RepositoryTask task = new RepositoryTask();
637        task.setRepositoryId( repositoryId );
638        task.setResourceFile( localFile );
639        task.setUpdateRelatedArtifacts( true );
640        task.setScanAll( false );
641
642        try
643        {
644            scheduler.queueTask( task );
645        }
646        catch ( TaskQueueException e )
647        {
648            log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
649                           + "']." );
650        }
651    }
652
653    private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
654        throws IOException
655    {
656
657        Files.copy( sourceFile.toPath(), new File( targetPath, targetFilename ).toPath(), StandardCopyOption.REPLACE_EXISTING,
658                    StandardCopyOption.COPY_ATTRIBUTES );
659
660        if ( fixChecksums )
661        {
662            fixChecksums( new File( targetPath, targetFilename ) );
663        }
664    }
665
666    /**
667     * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
668     */
669    private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
670                                        boolean fixChecksums, FileMetadata fileMetadata, String groupId,
671                                        String artifactId, String version, String packaging )
672        throws RepositoryMetadataException
673    {
674        List<String> availableVersions = new ArrayList<>();
675        String latestVersion = version;
676
677        File projectDir = new File( targetPath ).getParentFile();
678        File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
679
680        ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
681
682        if ( projectMetadataFile.exists() )
683        {
684            availableVersions = projectMetadata.getAvailableVersions();
685
686            Collections.sort( availableVersions, VersionComparator.getInstance() );
687
688            if ( !availableVersions.contains( version ) )
689            {
690                availableVersions.add( version );
691            }
692
693            latestVersion = availableVersions.get( availableVersions.size() - 1 );
694        }
695        else
696        {
697            availableVersions.add( version );
698
699            projectMetadata.setGroupId( groupId );
700            projectMetadata.setArtifactId( artifactId );
701        }
702
703        if ( projectMetadata.getGroupId() == null )
704        {
705            projectMetadata.setGroupId( groupId );
706        }
707
708        if ( projectMetadata.getArtifactId() == null )
709        {
710            projectMetadata.setArtifactId( artifactId );
711        }
712
713        projectMetadata.setLatestVersion( latestVersion );
714        projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
715        projectMetadata.setAvailableVersions( availableVersions );
716
717        if ( !VersionUtil.isSnapshot( version ) )
718        {
719            projectMetadata.setReleasedVersion( latestVersion );
720        }
721
722        RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
723
724        if ( fixChecksums )
725        {
726            fixChecksums( projectMetadataFile );
727        }
728    }
729
730    /**
731     * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
732     * if necessary.
733     */
734    private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
735                                        Date lastUpdatedTimestamp, String timestamp, int buildNumber,
736                                        boolean fixChecksums, FileMetadata fileMetadata, String groupId,
737                                        String artifactId, String version, String packaging )
738        throws RepositoryMetadataException
739    {
740        if ( !metadataFile.exists() )
741        {
742            metadata.setGroupId( groupId );
743            metadata.setArtifactId( artifactId );
744            metadata.setVersion( version );
745        }
746
747        if ( metadata.getSnapshotVersion() == null )
748        {
749            metadata.setSnapshotVersion( new SnapshotVersion() );
750        }
751
752        metadata.getSnapshotVersion().setBuildNumber( buildNumber );
753        metadata.getSnapshotVersion().setTimestamp( timestamp );
754        metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
755
756        RepositoryMetadataWriter.write( metadata, metadataFile );
757
758        if ( fixChecksums )
759        {
760            fixChecksums( metadataFile );
761        }
762    }
763
764
765}