View Javadoc
1   package org.apache.archiva.web.api;
2   /*
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
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 com.google.common.base.Predicate;
22  import com.google.common.collect.Iterables;
23  import org.apache.archiva.admin.model.RepositoryAdminException;
24  import org.apache.archiva.admin.model.admin.ArchivaAdministration;
25  import org.apache.archiva.admin.model.beans.ManagedRepository;
26  import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
27  import org.apache.archiva.metadata.model.facets.AuditEvent;
28  import org.apache.archiva.checksum.ChecksumAlgorithm;
29  import org.apache.archiva.checksum.ChecksummedFile;
30  import org.apache.archiva.common.utils.VersionComparator;
31  import org.apache.archiva.common.utils.VersionUtil;
32  import org.apache.archiva.maven2.metadata.MavenMetadataReader;
33  import org.apache.archiva.model.ArchivaRepositoryMetadata;
34  import org.apache.archiva.model.ArtifactReference;
35  import org.apache.archiva.model.SnapshotVersion;
36  import org.apache.archiva.redback.components.taskqueue.TaskQueueException;
37  import org.apache.archiva.repository.ManagedRepositoryContent;
38  import org.apache.archiva.repository.RepositoryContentFactory;
39  import org.apache.archiva.repository.RepositoryException;
40  import org.apache.archiva.repository.RepositoryNotFoundException;
41  import org.apache.archiva.repository.metadata.MetadataTools;
42  import org.apache.archiva.repository.metadata.RepositoryMetadataException;
43  import org.apache.archiva.repository.metadata.RepositoryMetadataWriter;
44  import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
45  import org.apache.archiva.rest.services.AbstractRestService;
46  import org.apache.archiva.scheduler.ArchivaTaskScheduler;
47  import org.apache.archiva.scheduler.repository.model.RepositoryTask;
48  import org.apache.archiva.web.model.FileMetadata;
49  import org.apache.archiva.xml.XMLException;
50  import org.apache.commons.io.FilenameUtils;
51  import org.apache.commons.io.IOUtils;
52  import org.apache.commons.lang.BooleanUtils;
53  import org.apache.commons.lang.StringUtils;
54  import org.apache.commons.lang.SystemUtils;
55  import org.apache.cxf.jaxrs.ext.multipart.Attachment;
56  import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
57  import org.apache.maven.model.Model;
58  import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
59  import org.slf4j.Logger;
60  import org.slf4j.LoggerFactory;
61  import org.springframework.stereotype.Service;
62  
63  import javax.inject.Inject;
64  import javax.inject.Named;
65  import javax.servlet.http.HttpServletRequest;
66  import javax.ws.rs.core.Context;
67  import javax.ws.rs.core.Response;
68  import java.io.File;
69  import java.io.FileOutputStream;
70  import java.io.FileWriter;
71  import java.io.IOException;
72  import java.nio.file.Files;
73  import java.nio.file.StandardCopyOption;
74  import java.text.DateFormat;
75  import java.text.SimpleDateFormat;
76  import java.util.ArrayList;
77  import java.util.Calendar;
78  import java.util.Collections;
79  import java.util.Date;
80  import java.util.Iterator;
81  import java.util.List;
82  import java.util.TimeZone;
83  import java.util.concurrent.CopyOnWriteArrayList;
84  
85  /**
86   * @author Olivier Lamy
87   */
88  @Service("fileUploadService#rest")
89  public class DefaultFileUploadService
90      extends AbstractRestService
91      implements FileUploadService
92  {
93      private Logger log = LoggerFactory.getLogger( getClass() );
94  
95      @Context
96      private HttpServletRequest httpServletRequest;
97  
98      @Inject
99      private ManagedRepositoryAdmin managedRepositoryAdmin;
100 
101     @Inject
102     private RepositoryContentFactory repositoryFactory;
103 
104     @Inject
105     private ArchivaAdministration archivaAdministration;
106 
107     private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
108 
109     @Inject
110     @Named(value = "archivaTaskScheduler#repository")
111     private ArchivaTaskScheduler scheduler;
112 
113     private String getStringValue( MultipartBody multipartBody, String attachmentId )
114         throws IOException
115     {
116         Attachment attachment = multipartBody.getAttachment( attachmentId );
117         return attachment == null ? "" : IOUtils.toString( attachment.getDataHandler().getInputStream() );
118     }
119 
120     @Override
121     public FileMetadata post( MultipartBody multipartBody )
122         throws ArchivaRestServiceException
123     {
124 
125         try
126         {
127 
128             String classifier = getStringValue( multipartBody, "classifier" );
129             String packaging = getStringValue( multipartBody, "packaging" );
130             // skygo: http header form pomFile was once sending 1 for true and void for false
131             // leading to permanent false value for pomFile if using toBoolean(); use , "1", ""
132             boolean pomFile = BooleanUtils.toBoolean( getStringValue( multipartBody, "pomFile" ) );
133 
134             Attachment file = multipartBody.getAttachment( "files[]" );
135 
136             //Content-Disposition: form-data; name="files[]"; filename="org.apache.karaf.features.command-2.2.2.jar"
137             String fileName = file.getContentDisposition().getParameter( "filename" );
138 
139             File tmpFile = File.createTempFile( "upload-artifact", ".tmp" );
140             tmpFile.deleteOnExit();
141             IOUtils.copy( file.getDataHandler().getInputStream(), new FileOutputStream( tmpFile ) );
142             FileMetadata fileMetadata = new FileMetadata( fileName, tmpFile.length(), "theurl" );
143             fileMetadata.setServerFileName( tmpFile.getPath() );
144             fileMetadata.setClassifier( classifier );
145             fileMetadata.setDeleteUrl( tmpFile.getName() );
146             fileMetadata.setPomFile( pomFile );
147             fileMetadata.setPackaging( packaging );
148 
149             log.info( "uploading file: {}", fileMetadata );
150 
151             List<FileMetadata> fileMetadatas = getSessionFilesList();
152 
153             fileMetadatas.add( fileMetadata );
154 
155             return fileMetadata;
156         }
157         catch ( IOException e )
158         {
159             throw new ArchivaRestServiceException( e.getMessage(),
160                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
161         }
162 
163     }
164 
165     /**
166      * FIXME must be per session synchronized not globally
167      *
168      * @return
169      */
170     protected synchronized List<FileMetadata> getSessionFilesList()
171     {
172         List<FileMetadata> fileMetadatas =
173             (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
174         if ( fileMetadatas == null )
175         {
176             fileMetadatas = new CopyOnWriteArrayList<>();
177             httpServletRequest.getSession().setAttribute( FILES_SESSION_KEY, fileMetadatas );
178         }
179         return fileMetadatas;
180     }
181 
182     @Override
183     public Boolean deleteFile( String fileName )
184         throws ArchivaRestServiceException
185     {
186         File file = new File( SystemUtils.getJavaIoTmpDir(), fileName );
187         log.debug( "delete file:{},exists:{}", file.getPath(), file.exists() );
188         boolean removed = getSessionFileMetadatas().remove( new FileMetadata( fileName ) );
189         // try with full name as ui only know the file name
190         if ( !removed )
191         {
192             /* unused */ getSessionFileMetadatas().remove( new FileMetadata( file.getPath() ) );
193         }
194         if ( file.exists() )
195         {
196             return file.delete();
197         }
198         return Boolean.FALSE;
199     }
200 
201     @Override
202     public Boolean clearUploadedFiles()
203         throws ArchivaRestServiceException
204     {
205         List<FileMetadata> fileMetadatas = new ArrayList( getSessionFileMetadatas() );
206         for ( FileMetadata fileMetadata : fileMetadatas )
207         {
208             deleteFile( new File( fileMetadata.getServerFileName() ).getPath() );
209         }
210         getSessionFileMetadatas().clear();
211         return Boolean.TRUE;
212     }
213 
214     @Override
215     public List<FileMetadata> getSessionFileMetadatas()
216         throws ArchivaRestServiceException
217     {
218         List<FileMetadata> fileMetadatas =
219             (List<FileMetadata>) httpServletRequest.getSession().getAttribute( FILES_SESSION_KEY );
220 
221         return fileMetadatas == null ? Collections.<FileMetadata>emptyList() : fileMetadatas;
222     }
223 
224     @Override
225     public Boolean save( String repositoryId, String groupId, String artifactId, String version, String packaging,
226                          boolean generatePom )
227         throws ArchivaRestServiceException
228     {
229         repositoryId = StringUtils.trim( repositoryId );
230         groupId = StringUtils.trim( groupId );
231         artifactId = StringUtils.trim( artifactId );
232         version = StringUtils.trim( version );
233         packaging = StringUtils.trim( packaging );
234 
235         List<FileMetadata> fileMetadatas = getSessionFilesList();
236         if ( fileMetadatas == null || fileMetadatas.isEmpty() )
237         {
238             return Boolean.FALSE;
239         }
240 
241         try
242         {
243             ManagedRepository managedRepository = managedRepositoryAdmin.getManagedRepository( repositoryId );
244 
245             if ( managedRepository == null )
246             {
247                 // TODO i18n ?
248                 throw new ArchivaRestServiceException( "Cannot find managed repository with id " + repositoryId,
249                                                        Response.Status.BAD_REQUEST.getStatusCode(), null );
250             }
251 
252             if ( VersionUtil.isSnapshot( version ) && !managedRepository.isSnapshots() )
253             {
254                 // TODO i18n ?
255                 throw new ArchivaRestServiceException(
256                     "Managed repository with id " + repositoryId + " do not accept snapshots",
257                     Response.Status.BAD_REQUEST.getStatusCode(), null );
258             }
259         }
260         catch ( RepositoryAdminException e )
261         {
262             throw new ArchivaRestServiceException( e.getMessage(),
263                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
264         }
265 
266         // get from the session file with groupId/artifactId
267 
268         Iterable<FileMetadata> filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
269         {
270             public boolean apply( FileMetadata fileMetadata )
271             {
272                 return fileMetadata != null && !fileMetadata.isPomFile();
273             }
274         } );
275         Iterator<FileMetadata> iterator = filesToAdd.iterator();
276         boolean pomGenerated = false;
277         while ( iterator.hasNext() )
278         {
279             FileMetadata fileMetadata = iterator.next();
280             log.debug( "fileToAdd: {}", fileMetadata );
281             saveFile( repositoryId, fileMetadata, generatePom && !pomGenerated, groupId, artifactId, version,
282                       packaging );
283             pomGenerated = true;
284             deleteFile( fileMetadata.getServerFileName() );
285         }
286 
287         filesToAdd = Iterables.filter( fileMetadatas, new Predicate<FileMetadata>()
288         {
289             @Override
290             public boolean apply( FileMetadata fileMetadata )
291             {
292                 return fileMetadata != null && fileMetadata.isPomFile();
293             }
294         } );
295 
296         iterator = filesToAdd.iterator();
297         while ( iterator.hasNext() )
298         {
299             FileMetadata fileMetadata = iterator.next();
300             log.debug( "fileToAdd: {}", fileMetadata );
301             savePomFile( repositoryId, fileMetadata, groupId, artifactId, version, packaging );
302             deleteFile( fileMetadata.getServerFileName() );
303         }
304 
305         return Boolean.TRUE;
306     }
307 
308     protected void savePomFile( String repositoryId, FileMetadata fileMetadata, String groupId, String artifactId,
309                                 String version, String packaging )
310         throws ArchivaRestServiceException
311     {
312 
313         try
314         {
315             boolean fixChecksums =
316                 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
317 
318             ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
319 
320             ArtifactReference artifactReference = new ArtifactReference();
321             artifactReference.setArtifactId( artifactId );
322             artifactReference.setGroupId( groupId );
323             artifactReference.setVersion( version );
324             artifactReference.setClassifier( fileMetadata.getClassifier() );
325             artifactReference.setType( packaging );
326 
327             ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
328 
329             String artifactPath = repository.toPath( artifactReference );
330 
331             int lastIndex = artifactPath.lastIndexOf( '/' );
332 
333             String path = artifactPath.substring( 0, lastIndex );
334             File targetPath = new File( repoConfig.getLocation(), path );
335 
336             String pomFilename = artifactPath.substring( lastIndex + 1 );
337             if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
338             {
339                 pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
340             }
341             pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
342 
343             copyFile( new File( fileMetadata.getServerFileName() ), targetPath, pomFilename, fixChecksums );
344             triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
345             queueRepositoryTask( repoConfig.getId(), new File( targetPath, pomFilename ) );
346         }
347         catch ( IOException ie )
348         {
349             throw new ArchivaRestServiceException( "Error encountered while uploading pom file: " + ie.getMessage(),
350                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie );
351         }
352         catch ( RepositoryException rep )
353         {
354             throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
355                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rep );
356         }
357         catch ( RepositoryAdminException e )
358         {
359             throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
360                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
361         }
362     }
363 
364     protected void saveFile( String repositoryId, FileMetadata fileMetadata, boolean generatePom, String groupId,
365                              String artifactId, String version, String packaging )
366         throws ArchivaRestServiceException
367     {
368         try
369         {
370 
371             ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
372 
373             ArtifactReference artifactReference = new ArtifactReference();
374             artifactReference.setArtifactId( artifactId );
375             artifactReference.setGroupId( groupId );
376             artifactReference.setVersion( version );
377             artifactReference.setClassifier( fileMetadata.getClassifier() );
378             artifactReference.setType(
379                 StringUtils.isEmpty( fileMetadata.getPackaging() ) ? packaging : fileMetadata.getPackaging() );
380 
381             ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
382 
383             String artifactPath = repository.toPath( artifactReference );
384 
385             int lastIndex = artifactPath.lastIndexOf( '/' );
386 
387             String path = artifactPath.substring( 0, lastIndex );
388             File targetPath = new File( repoConfig.getLocation(), path );
389 
390             log.debug( "artifactPath: {} found targetPath: {}", artifactPath, targetPath );
391 
392             Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
393             int newBuildNumber = -1;
394             String timestamp = null;
395 
396             File versionMetadataFile = new File( targetPath, MetadataTools.MAVEN_METADATA );
397             ArchivaRepositoryMetadata versionMetadata = getMetadata( versionMetadataFile );
398 
399             if ( VersionUtil.isSnapshot( version ) )
400             {
401                 TimeZone timezone = TimeZone.getTimeZone( "UTC" );
402                 DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
403                 fmt.setTimeZone( timezone );
404                 timestamp = fmt.format( lastUpdatedTimestamp );
405                 if ( versionMetadata.getSnapshotVersion() != null )
406                 {
407                     newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
408                 }
409                 else
410                 {
411                     newBuildNumber = 1;
412                 }
413             }
414 
415             if ( !targetPath.exists() )
416             {
417                 targetPath.mkdirs();
418             }
419 
420             String filename = artifactPath.substring( lastIndex + 1 );
421             if ( VersionUtil.isSnapshot( version ) )
422             {
423                 filename = filename.replaceAll( VersionUtil.SNAPSHOT, timestamp + "-" + newBuildNumber );
424             }
425 
426             boolean fixChecksums =
427                 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
428 
429             try
430             {
431                 File targetFile = new File( targetPath, filename );
432                 if ( targetFile.exists() && !VersionUtil.isSnapshot( version ) && repoConfig.isBlockRedeployments() )
433                 {
434                     throw new ArchivaRestServiceException(
435                         "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
436                         Response.Status.BAD_REQUEST.getStatusCode(), null );
437                 }
438                 else
439                 {
440                     copyFile( new File( fileMetadata.getServerFileName() ), targetPath, filename, fixChecksums );
441                     triggerAuditEvent( repository.getId(), path + "/" + filename, AuditEvent.UPLOAD_FILE );
442                     queueRepositoryTask( repository.getId(), targetFile );
443                 }
444             }
445             catch ( IOException ie )
446             {
447                 log.error( "IOException copying file: {}", ie.getMessage(), ie );
448                 throw new ArchivaRestServiceException(
449                     "Overwriting released artifacts in repository '" + repoConfig.getId() + "' is not allowed.",
450                     Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie );
451             }
452 
453             if ( generatePom )
454             {
455                 String pomFilename = filename;
456                 if ( StringUtils.isNotEmpty( fileMetadata.getClassifier() ) )
457                 {
458                     pomFilename = StringUtils.remove( pomFilename, "-" + fileMetadata.getClassifier() );
459                 }
460                 pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
461 
462                 try
463                 {
464                     File generatedPomFile =
465                         createPom( targetPath, pomFilename, fileMetadata, groupId, artifactId, version, packaging );
466                     triggerAuditEvent( repoConfig.getId(), path + "/" + pomFilename, AuditEvent.UPLOAD_FILE );
467                     if ( fixChecksums )
468                     {
469                         fixChecksums( generatedPomFile );
470                     }
471                     queueRepositoryTask( repoConfig.getId(), generatedPomFile );
472                 }
473                 catch ( IOException ie )
474                 {
475                     throw new ArchivaRestServiceException(
476                         "Error encountered while writing pom file: " + ie.getMessage(),
477                         Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie );
478                 }
479             }
480 
481             // explicitly update only if metadata-updater consumer is not enabled!
482             if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) )
483             {
484                 updateProjectMetadata( targetPath.getAbsolutePath(), lastUpdatedTimestamp, timestamp, newBuildNumber,
485                                        fixChecksums, fileMetadata, groupId, artifactId, version, packaging );
486 
487                 if ( VersionUtil.isSnapshot( version ) )
488                 {
489                     updateVersionMetadata( versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
490                                            newBuildNumber, fixChecksums, fileMetadata, groupId, artifactId, version,
491                                            packaging );
492                 }
493             }
494         }
495         catch ( RepositoryNotFoundException re )
496         {
497             throw new ArchivaRestServiceException( "Target repository cannot be found: " + re.getMessage(),
498                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), re );
499         }
500         catch ( RepositoryException rep )
501         {
502             throw new ArchivaRestServiceException( "Repository exception: " + rep.getMessage(),
503                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rep );
504         }
505         catch ( RepositoryAdminException e )
506         {
507             throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(),
508                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
509         }
510     }
511 
512     private ArchivaRepositoryMetadata getMetadata( File metadataFile )
513         throws RepositoryMetadataException
514     {
515         ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
516         if ( metadataFile.exists() )
517         {
518             try
519             {
520                 metadata = MavenMetadataReader.read( metadataFile );
521             }
522             catch ( XMLException e )
523             {
524                 throw new RepositoryMetadataException( e.getMessage(), e );
525             }
526         }
527         return metadata;
528     }
529 
530     private File createPom( File targetPath, String filename, FileMetadata fileMetadata, String groupId,
531                             String artifactId, String version, String packaging )
532         throws IOException
533     {
534         Model projectModel = new Model();
535         projectModel.setModelVersion( "4.0.0" );
536         projectModel.setGroupId( groupId );
537         projectModel.setArtifactId( artifactId );
538         projectModel.setVersion( version );
539         projectModel.setPackaging( packaging );
540 
541         File pomFile = new File( targetPath, filename );
542         MavenXpp3Writer writer = new MavenXpp3Writer();
543 
544         try (FileWriter w = new FileWriter( pomFile ))
545         {
546             writer.write( w, projectModel );
547         }
548 
549         return pomFile;
550     }
551 
552     private void fixChecksums( File file )
553     {
554         ChecksummedFile checksum = new ChecksummedFile( file );
555         checksum.fixChecksums( algorithms );
556     }
557 
558     private void queueRepositoryTask( String repositoryId, File localFile )
559     {
560         RepositoryTask task = new RepositoryTask();
561         task.setRepositoryId( repositoryId );
562         task.setResourceFile( localFile );
563         task.setUpdateRelatedArtifacts( true );
564         task.setScanAll( false );
565 
566         try
567         {
568             scheduler.queueTask( task );
569         }
570         catch ( TaskQueueException e )
571         {
572             log.error( "Unable to queue repository task to execute consumers on resource file ['" + localFile.getName()
573                            + "']." );
574         }
575     }
576 
577     private void copyFile( File sourceFile, File targetPath, String targetFilename, boolean fixChecksums )
578         throws IOException
579     {
580 
581         Files.copy( sourceFile.toPath(), new File( targetPath, targetFilename ).toPath(), StandardCopyOption.REPLACE_EXISTING,
582                     StandardCopyOption.COPY_ATTRIBUTES );
583 
584         if ( fixChecksums )
585         {
586             fixChecksums( new File( targetPath, targetFilename ) );
587         }
588     }
589 
590     /**
591      * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
592      */
593     private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
594                                         boolean fixChecksums, FileMetadata fileMetadata, String groupId,
595                                         String artifactId, String version, String packaging )
596         throws RepositoryMetadataException
597     {
598         List<String> availableVersions = new ArrayList<>();
599         String latestVersion = version;
600 
601         File projectDir = new File( targetPath ).getParentFile();
602         File projectMetadataFile = new File( projectDir, MetadataTools.MAVEN_METADATA );
603 
604         ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
605 
606         if ( projectMetadataFile.exists() )
607         {
608             availableVersions = projectMetadata.getAvailableVersions();
609 
610             Collections.sort( availableVersions, VersionComparator.getInstance() );
611 
612             if ( !availableVersions.contains( version ) )
613             {
614                 availableVersions.add( version );
615             }
616 
617             latestVersion = availableVersions.get( availableVersions.size() - 1 );
618         }
619         else
620         {
621             availableVersions.add( version );
622 
623             projectMetadata.setGroupId( groupId );
624             projectMetadata.setArtifactId( artifactId );
625         }
626 
627         if ( projectMetadata.getGroupId() == null )
628         {
629             projectMetadata.setGroupId( groupId );
630         }
631 
632         if ( projectMetadata.getArtifactId() == null )
633         {
634             projectMetadata.setArtifactId( artifactId );
635         }
636 
637         projectMetadata.setLatestVersion( latestVersion );
638         projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
639         projectMetadata.setAvailableVersions( availableVersions );
640 
641         if ( !VersionUtil.isSnapshot( version ) )
642         {
643             projectMetadata.setReleasedVersion( latestVersion );
644         }
645 
646         RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile );
647 
648         if ( fixChecksums )
649         {
650             fixChecksums( projectMetadataFile );
651         }
652     }
653 
654     /**
655      * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
656      * if necessary.
657      */
658     private void updateVersionMetadata( ArchivaRepositoryMetadata metadata, File metadataFile,
659                                         Date lastUpdatedTimestamp, String timestamp, int buildNumber,
660                                         boolean fixChecksums, FileMetadata fileMetadata, String groupId,
661                                         String artifactId, String version, String packaging )
662         throws RepositoryMetadataException
663     {
664         if ( !metadataFile.exists() )
665         {
666             metadata.setGroupId( groupId );
667             metadata.setArtifactId( artifactId );
668             metadata.setVersion( version );
669         }
670 
671         if ( metadata.getSnapshotVersion() == null )
672         {
673             metadata.setSnapshotVersion( new SnapshotVersion() );
674         }
675 
676         metadata.getSnapshotVersion().setBuildNumber( buildNumber );
677         metadata.getSnapshotVersion().setTimestamp( timestamp );
678         metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
679 
680         RepositoryMetadataWriter.write( metadata, metadataFile );
681 
682         if ( fixChecksums )
683         {
684             fixChecksums( metadataFile );
685         }
686     }
687 
688 
689 }