]> source.dussan.org Git - archiva.git/blob
c091095bb3acbef7a5426231e7fa64644a44b252
[archiva.git] /
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 org.apache.archiva.admin.model.RepositoryAdminException;
22 import org.apache.archiva.admin.model.admin.ArchivaAdministration;
23 import org.apache.archiva.checksum.ChecksumAlgorithm;
24 import org.apache.archiva.checksum.ChecksumUtil;
25 import org.apache.archiva.checksum.ChecksummedFile;
26 import org.apache.archiva.common.utils.VersionComparator;
27 import org.apache.archiva.common.utils.VersionUtil;
28 import org.apache.archiva.components.taskqueue.TaskQueueException;
29 import org.apache.archiva.configuration.ArchivaConfiguration;
30 import org.apache.archiva.metadata.model.facets.AuditEvent;
31 import org.apache.archiva.model.ArchivaRepositoryMetadata;
32 import org.apache.archiva.model.SnapshotVersion;
33 import org.apache.archiva.repository.ReleaseScheme;
34 import org.apache.archiva.repository.Repository;
35 import org.apache.archiva.repository.RepositoryException;
36 import org.apache.archiva.repository.RepositoryRegistry;
37 import org.apache.archiva.repository.RepositoryType;
38 import org.apache.archiva.repository.content.ItemSelector;
39 import org.apache.archiva.repository.content.base.ArchivaItemSelector;
40 import org.apache.archiva.repository.metadata.MetadataReader;
41 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
42 import org.apache.archiva.repository.metadata.base.MetadataTools;
43 import org.apache.archiva.repository.metadata.base.RepositoryMetadataWriter;
44 import org.apache.archiva.repository.storage.StorageAsset;
45 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
46 import org.apache.archiva.rest.services.AbstractRestService;
47 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
48 import org.apache.archiva.scheduler.repository.model.RepositoryTask;
49 import org.apache.archiva.web.model.FileMetadata;
50 import org.apache.commons.io.FilenameUtils;
51 import org.apache.commons.io.IOUtils;
52 import org.apache.commons.lang3.BooleanUtils;
53 import org.apache.commons.lang3.StringUtils;
54 import org.apache.commons.lang3.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.annotation.PostConstruct;
64 import javax.inject.Inject;
65 import javax.inject.Named;
66 import javax.servlet.http.HttpServletRequest;
67 import javax.servlet.http.HttpSession;
68 import javax.ws.rs.core.Context;
69 import javax.ws.rs.core.Response;
70 import java.io.FileOutputStream;
71 import java.io.IOException;
72 import java.io.OutputStreamWriter;
73 import java.io.Writer;
74 import java.net.URLDecoder;
75 import java.nio.file.FileSystems;
76 import java.nio.file.Files;
77 import java.nio.file.Path;
78 import java.nio.file.Paths;
79 import java.text.DateFormat;
80 import java.text.SimpleDateFormat;
81 import java.util.ArrayList;
82 import java.util.Calendar;
83 import java.util.Collections;
84 import java.util.Date;
85 import java.util.Iterator;
86 import java.util.List;
87 import java.util.TimeZone;
88 import java.util.concurrent.CopyOnWriteArrayList;
89
90 /**
91  *
92  * Service for uploading files to the repository.
93  *
94  * @author Olivier Lamy
95  * @author Martin Stockhammer
96  */
97 @Service("fileUploadService#rest")
98 public class DefaultFileUploadService
99         extends AbstractRestService
100         implements FileUploadService {
101     private Logger log = LoggerFactory.getLogger(getClass());
102
103     @Context
104     private HttpServletRequest httpServletRequest;
105
106     @Inject
107     private ArchivaAdministration archivaAdministration;
108
109     @Inject
110     ArchivaConfiguration configuration;
111
112     private List<ChecksumAlgorithm> algorithms;
113
114     private final String FS = FileSystems.getDefault().getSeparator();
115
116     @Inject
117     @Named(value = "archivaTaskScheduler#repository")
118     private ArchivaTaskScheduler<RepositoryTask> scheduler;
119
120     @Inject
121     private RepositoryRegistry repositoryRegistry;
122
123     private String getStringValue(MultipartBody multipartBody, String attachmentId)
124             throws IOException {
125         Attachment attachment = multipartBody.getAttachment(attachmentId);
126         return attachment == null ? "" :
127                 StringUtils.trim(URLDecoder.decode(IOUtils.toString(attachment.getDataHandler().getInputStream(), "UTF-8"), "UTF-8"));
128     }
129
130     @PostConstruct
131     private void initialize() {
132         algorithms = ChecksumUtil.getAlgorithms(configuration.getConfiguration().getArchivaRuntimeConfiguration().getChecksumTypes());
133     }
134
135     @Override
136     public FileMetadata post(MultipartBody multipartBody)
137             throws ArchivaRestServiceException {
138
139         try {
140
141             String classifier = getStringValue(multipartBody, "classifier");
142             String packaging = getStringValue(multipartBody, "packaging");
143
144             checkParamChars("classifier", classifier);
145             checkParamChars("packaging", packaging);
146
147             // skygo: http header form pomFile was once sending 1 for true and void for false
148             // leading to permanent false value for pomFile if using toBoolean(); use , "1", ""
149
150             boolean pomFile = false;
151             try {
152                 pomFile = BooleanUtils.toBoolean(getStringValue(multipartBody, "pomFile"));
153             } catch (IllegalArgumentException ex) {
154                 ArchivaRestServiceException e = new ArchivaRestServiceException("Bad value for boolean pomFile field.", null);
155                 e.setHttpErrorCode(422);
156                 e.setFieldName("pomFile");
157                 e.setErrorKey("fileupload.malformed.pomFile");
158                 throw e;
159             }
160
161             Attachment file = multipartBody.getAttachment("files[]");
162
163             //Content-Disposition: form-data; name="files[]"; filename="org.apache.karaf.features.command-2.2.2.jar"
164             String fileName = file.getContentDisposition().getParameter("filename");
165             Path fileNamePath = Paths.get(fileName);
166             if (!fileName.equals(fileNamePath.getFileName().toString())) {
167                 ArchivaRestServiceException e = new ArchivaRestServiceException("Bad filename in upload content: " + fileName + " - File traversal chars (..|/) are not allowed"
168                         , null);
169                 e.setHttpErrorCode(422);
170                 e.setErrorKey("fileupload.malformed.filename");
171                 throw e;
172             }
173
174             Path tmpFile = Files.createTempFile("upload-artifact", ".tmp");
175             tmpFile.toFile().deleteOnExit();
176             IOUtils.copy(file.getDataHandler().getInputStream(), new FileOutputStream(tmpFile.toFile()));
177             FileMetadata fileMetadata = new FileMetadata(fileName, Files.size(tmpFile), "theurl");
178             fileMetadata.setServerFileName(tmpFile.toString());
179             fileMetadata.setClassifier(classifier);
180             fileMetadata.setDeleteUrl(tmpFile.getFileName().toString());
181             fileMetadata.setPomFile(pomFile);
182             fileMetadata.setPackaging(packaging);
183
184             log.info("uploading file: {}", fileMetadata);
185
186             List<FileMetadata> fileMetadatas = getSessionFilesList();
187
188             fileMetadatas.add(fileMetadata);
189
190             return fileMetadata;
191         } catch (IOException e) {
192             throw new ArchivaRestServiceException(e.getMessage(),
193                     Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e);
194         }
195
196     }
197
198     /**
199      * @return The file list from the session.
200      */
201     @SuppressWarnings("unchecked")
202     protected List<FileMetadata> getSessionFilesList() {
203         final HttpSession session = httpServletRequest.getSession();
204         List<FileMetadata> fileMetadata = (List<FileMetadata>) session.getAttribute(FILES_SESSION_KEY);
205         // Double check with synchronization, we assume, that httpServletRequest is
206         // fully initialized (no volatile)
207         if (fileMetadata == null) {
208             synchronized (session) {
209                 fileMetadata = (List<FileMetadata>) session.getAttribute(FILES_SESSION_KEY);
210                 if (fileMetadata == null) {
211                     fileMetadata = new CopyOnWriteArrayList<>();
212                     session.setAttribute(FILES_SESSION_KEY, fileMetadata);
213                 }
214             }
215         }
216         return fileMetadata;
217     }
218
219     @Override
220     public Boolean deleteFile(String fileName)
221             throws ArchivaRestServiceException {
222         log.debug("Deleting file {}", fileName);
223         // we make sure, that there are no other path components in the filename:
224         String checkedFileName = Paths.get(fileName).getFileName().toString();
225         Path file = SystemUtils.getJavaIoTmpDir().toPath().resolve(checkedFileName);
226         log.debug("delete file:{},exists:{}", file, Files.exists(file));
227         boolean removed = getSessionFileMetadatas().remove(new FileMetadata(fileName));
228         // try with full name as ui only know the file name
229         if (!removed) {
230             removed = getSessionFileMetadatas().remove(new FileMetadata(file.toString()));
231         }
232         if (removed) {
233             try {
234                 Files.deleteIfExists(file);
235                 return Boolean.TRUE;
236             } catch (IOException e) {
237                 log.error("Could not delete file {}: {}", file, e.getMessage(), e);
238             }
239         }
240         return Boolean.FALSE;
241     }
242
243     @Override
244     public Boolean clearUploadedFiles()
245             throws ArchivaRestServiceException {
246         List<FileMetadata> fileMetadatas = new ArrayList<>(getSessionFileMetadatas());
247         for (FileMetadata fileMetadata : fileMetadatas) {
248             deleteFile(Paths.get(fileMetadata.getServerFileName()).toString());
249         }
250         getSessionFileMetadatas().clear();
251         return Boolean.TRUE;
252     }
253
254     @Override
255     public List<FileMetadata> getSessionFileMetadatas()
256             throws ArchivaRestServiceException {
257         return getSessionFilesList();
258     }
259
260
261     private boolean hasValidChars(String checkString) {
262         if (checkString.contains(FS)) {
263             return false;
264         }
265         if (checkString.contains("../")) {
266             return false;
267         }
268         if (checkString.contains("/..")) {
269             return false;
270         }
271         return true;
272     }
273
274     private void checkParamChars(String param, String value) throws ArchivaRestServiceException {
275         if (!hasValidChars(value)) {
276             ArchivaRestServiceException e = new ArchivaRestServiceException("Bad characters in " + param, null);
277             e.setHttpErrorCode(422);
278             e.setErrorKey("fileupload.malformed.param");
279             e.setFieldName(param);
280             throw e;
281         }
282     }
283
284     @Override
285     public Boolean save(String repositoryId, String groupId, String artifactId, String version, String packaging,
286                         boolean generatePom)
287             throws ArchivaRestServiceException {
288         repositoryId = StringUtils.trim(repositoryId);
289         groupId = StringUtils.trim(groupId);
290         artifactId = StringUtils.trim(artifactId);
291         version = StringUtils.trim(version);
292         packaging = StringUtils.trim(packaging);
293
294         checkParamChars("repositoryId", repositoryId);
295         checkParamChars("groupId", groupId);
296         checkParamChars("artifactId", artifactId);
297         checkParamChars("version", version);
298         checkParamChars("packaging", packaging);
299
300
301         List<FileMetadata> fileMetadatas = getSessionFilesList();
302         if (fileMetadatas == null || fileMetadatas.isEmpty()) {
303             return Boolean.FALSE;
304         }
305
306         org.apache.archiva.repository.ManagedRepository repository = repositoryRegistry.getManagedRepository(repositoryId);
307         if (repository == null) {
308             // TODO i18n ?
309             throw new ArchivaRestServiceException("Cannot find managed repository with id " + repositoryId,
310                     Response.Status.BAD_REQUEST.getStatusCode(), null);
311         }
312
313         if (VersionUtil.isSnapshot(version) && !repository.getActiveReleaseSchemes().contains( ReleaseScheme.SNAPSHOT )) {
314             // TODO i18n ?
315             throw new ArchivaRestServiceException(
316                     "Managed repository with id " + repositoryId + " do not accept snapshots",
317                     Response.Status.BAD_REQUEST.getStatusCode(), null);
318         }
319
320         // get from the session file with groupId/artifactId
321
322         Iterator<FileMetadata> iterator = fileMetadatas.stream( )
323             .filter( fileMetadata -> fileMetadata != null && !fileMetadata.isPomFile( ) )
324             .iterator( );
325         boolean pomGenerated = false;
326         while (iterator.hasNext()) {
327             FileMetadata fileMetadata = iterator.next();
328             log.debug("fileToAdd: {}", fileMetadata);
329             saveFile(repositoryId, fileMetadata, generatePom && !pomGenerated, groupId, artifactId, version,
330                     packaging);
331             pomGenerated = true;
332             deleteFile(fileMetadata.getServerFileName());
333         }
334
335         iterator = fileMetadatas.stream( ).filter( fileMetadata -> fileMetadata != null && fileMetadata.isPomFile( ) )
336             .iterator( );
337         while (iterator.hasNext()) {
338             FileMetadata fileMetadata = iterator.next();
339             log.debug("fileToAdd: {}", fileMetadata);
340             savePomFile(repositoryId, fileMetadata, groupId, artifactId, version, packaging);
341             deleteFile(fileMetadata.getServerFileName());
342         }
343
344         return Boolean.TRUE;
345     }
346
347     protected void savePomFile(String repositoryId, FileMetadata fileMetadata, String groupId, String artifactId,
348                                String version, String packaging)
349             throws ArchivaRestServiceException {
350
351         log.debug("Saving POM");
352         try {
353             boolean fixChecksums =
354                     !(archivaAdministration.getKnownContentConsumers().contains("create-missing-checksums"));
355
356             org.apache.archiva.repository.ManagedRepository repository = repositoryRegistry.getManagedRepository(repositoryId);
357             ItemSelector selector = ArchivaItemSelector.builder( )
358                 .withNamespace( groupId )
359                 .withProjectId( artifactId )
360                 .withArtifactId( artifactId )
361                 .withArtifactVersion( version )
362                 .withExtension( packaging ).build();
363
364             StorageAsset pomPath = repository.getContent( ).getItem( selector ).getAsset();
365             StorageAsset targetPath = pomPath.getParent();
366
367             String pomFilename = pomPath.getName();
368             if (StringUtils.isNotEmpty(fileMetadata.getClassifier())) {
369                 pomFilename = StringUtils.remove(pomFilename, "-" + fileMetadata.getClassifier());
370             }
371             pomFilename = FilenameUtils.removeExtension(pomFilename) + ".pom";
372
373             copyFile(Paths.get(fileMetadata.getServerFileName()), targetPath, pomFilename, fixChecksums);
374             triggerAuditEvent(repository.getId(), targetPath.resolve(pomFilename).toString(), AuditEvent.UPLOAD_FILE);
375             queueRepositoryTask(repository.getId(), targetPath.resolve(pomFilename));
376             log.debug("Finished Saving POM");
377         } catch (IOException ie) {
378             log.error("IOException for POM {}", ie.getMessage());
379             throw new ArchivaRestServiceException("Error encountered while uploading pom file: " + ie.getMessage(),
380                     Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie);
381         }
382         catch (RepositoryAdminException e) {
383             log.error("RepositoryAdminException for POM {}", e.getMessage());
384             throw new ArchivaRestServiceException("RepositoryAdmin exception: " + e.getMessage(),
385                     Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e);
386         }
387     }
388
389     protected void saveFile(String repositoryId, FileMetadata fileMetadata, boolean generatePom, String groupId,
390                             String artifactId, String version, String packaging)
391             throws ArchivaRestServiceException {
392         log.debug("Saving file");
393         try {
394
395             org.apache.archiva.repository.ManagedRepository repository = repositoryRegistry.getManagedRepository(repositoryId);
396
397             ItemSelector selector = ArchivaItemSelector.builder( )
398                 .withNamespace( groupId )
399                 .withProjectId( artifactId )
400                 .withArtifactId( artifactId )
401                 .withArtifactVersion( version )
402                 .withExtension( packaging ).build();
403
404             StorageAsset artifactPath = repository.getContent( ).getItem( selector ).getAsset();
405             StorageAsset targetPath = artifactPath.getParent();
406
407             log.debug("artifactPath: {} found targetPath: {}", artifactPath, targetPath);
408
409             Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
410             int newBuildNumber = -1;
411             String timestamp = null;
412
413             StorageAsset versionMetadataFile = targetPath.resolve(MetadataTools.MAVEN_METADATA);
414             ArchivaRepositoryMetadata versionMetadata = getMetadata(versionMetadataFile);
415
416             if (VersionUtil.isSnapshot(version)) {
417                 TimeZone timezone = TimeZone.getTimeZone("UTC");
418                 DateFormat fmt = new SimpleDateFormat("yyyyMMdd.HHmmss");
419                 fmt.setTimeZone(timezone);
420                 timestamp = fmt.format(lastUpdatedTimestamp);
421                 if (versionMetadata.getSnapshotVersion() != null) {
422                     newBuildNumber = versionMetadata.getSnapshotVersion().getBuildNumber() + 1;
423                 } else {
424                     newBuildNumber = 1;
425                 }
426             }
427
428             if (!targetPath.exists()) {
429                 targetPath.create();
430             }
431
432             String filename = artifactPath.getName().toString();
433             if (VersionUtil.isSnapshot(version)) {
434                 filename = filename.replaceAll(VersionUtil.SNAPSHOT, timestamp + "-" + newBuildNumber);
435             }
436
437             // We always fix checksums for newly uploaded files, even if the content consumer is active.
438             boolean fixChecksums = true;
439             // !(archivaAdministration.getKnownContentConsumers().contains("create-missing-checksums"));
440
441             try {
442                 StorageAsset targetFile = targetPath.resolve(filename);
443                 if (targetFile.exists() && !VersionUtil.isSnapshot(version) && repository.blocksRedeployments()) {
444                     throw new ArchivaRestServiceException(
445                             "Overwriting released artifacts in repository '" + repository.getId() + "' is not allowed.",
446                             Response.Status.BAD_REQUEST.getStatusCode(), null);
447                 } else {
448                     copyFile(Paths.get(fileMetadata.getServerFileName()), targetPath, filename, fixChecksums);
449                     triggerAuditEvent(repository.getId(), artifactPath.toString(), AuditEvent.UPLOAD_FILE);
450                     queueRepositoryTask(repository.getId(), targetFile);
451                 }
452             } catch (IOException ie) {
453                 log.error("IOException copying file: {}", ie.getMessage(), ie);
454                 throw new ArchivaRestServiceException(
455                         "Overwriting released artifacts in repository '" + repository.getId() + "' is not allowed.",
456                         Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), ie);
457             }
458
459             if (generatePom) {
460                 String pomFilename = filename;
461                 if (StringUtils.isNotEmpty(fileMetadata.getClassifier())) {
462                     pomFilename = StringUtils.remove(pomFilename, "-" + fileMetadata.getClassifier());
463                 }
464                 pomFilename = FilenameUtils.removeExtension(pomFilename) + ".pom";
465
466                 try {
467                     StorageAsset generatedPomFile =
468                             createPom(targetPath, pomFilename, fileMetadata, groupId, artifactId, version, packaging);
469                     triggerAuditEvent(repository.getId(), targetPath.resolve(pomFilename).toString(), AuditEvent.UPLOAD_FILE);
470                     if (fixChecksums) {
471                         fixChecksums(generatedPomFile);
472                     }
473                     queueRepositoryTask(repository.getId(), generatedPomFile);
474                 } catch (IOException ie) {
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                 updateProjectMetadata(targetPath, lastUpdatedTimestamp, timestamp, newBuildNumber,
484                         fixChecksums, fileMetadata, groupId, artifactId, version, packaging);
485
486                 if (VersionUtil.isSnapshot(version)) {
487                     updateVersionMetadata(versionMetadata, versionMetadataFile, lastUpdatedTimestamp, timestamp,
488                             newBuildNumber, fixChecksums, fileMetadata, groupId, artifactId, version,
489                             packaging);
490                 }
491             }
492         }
493         catch (RepositoryException rep) {
494             log.error("RepositoryException during save {}", rep.getMessage());
495             throw new ArchivaRestServiceException("Repository exception: " + rep.getMessage(),
496                     Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), rep);
497         } catch (RepositoryAdminException e) {
498             log.error("RepositoryAdminException during save {}", e.getMessage());
499             throw new ArchivaRestServiceException("RepositoryAdmin exception: " + e.getMessage(),
500                     Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e);
501         } catch (IOException e) {
502             log.error("IOException during save {}", e.getMessage());
503             throw new ArchivaRestServiceException("Repository exception " + e.getMessage(),
504                     Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e);
505         }
506     }
507
508     private ArchivaRepositoryMetadata getMetadata(StorageAsset metadataFile)
509             throws RepositoryMetadataException {
510         ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
511         if (metadataFile.exists()) {
512             Repository repo = repositoryRegistry.getRepositoryOfAsset( metadataFile );
513             RepositoryType type = repo == null ? RepositoryType.MAVEN : repo.getType( );
514             MetadataReader metadataReader = repositoryRegistry.getMetadataReader( type );
515             metadata = metadataReader.read(metadataFile);
516         }
517         return metadata;
518     }
519
520     private StorageAsset createPom(StorageAsset targetPath, String filename, FileMetadata fileMetadata, String groupId,
521                            String artifactId, String version, String packaging)
522             throws IOException {
523         Model projectModel = new Model();
524         projectModel.setModelVersion("4.0.0");
525         projectModel.setGroupId(groupId);
526         projectModel.setArtifactId(artifactId);
527         projectModel.setVersion(version);
528         projectModel.setPackaging(packaging);
529
530         StorageAsset pomFile = targetPath.resolve(filename);
531         MavenXpp3Writer writer = new MavenXpp3Writer();
532
533         try (Writer w = new OutputStreamWriter(pomFile.getWriteStream(true))) {
534             writer.write(w, projectModel);
535         }
536
537         return pomFile;
538     }
539
540     private void fixChecksums(StorageAsset file) {
541         ChecksummedFile checksum = new ChecksummedFile(file.getFilePath());
542         checksum.fixChecksums(algorithms);
543     }
544
545     private void queueRepositoryTask(String repositoryId, StorageAsset localFile) {
546         RepositoryTask task = new RepositoryTask();
547         task.setRepositoryId(repositoryId);
548         task.setResourceFile(localFile);
549         task.setUpdateRelatedArtifacts(true);
550         task.setScanAll(false);
551
552         try {
553             scheduler.queueTask(task);
554         } catch (TaskQueueException e) {
555             log.error("Unable to queue repository task to execute consumers on resource file ['{}"
556                     + "'].", localFile.getName());
557         }
558     }
559
560     private void copyFile(Path sourceFile, StorageAsset targetPath, String targetFilename, boolean fixChecksums)
561             throws IOException {
562
563         targetPath.resolve(targetFilename).replaceDataFromFile(sourceFile);
564
565         if (fixChecksums) {
566             fixChecksums(targetPath.resolve(targetFilename));
567         }
568     }
569
570     /**
571      * Update artifact level metadata. If it does not exist, create the metadata and fix checksums if necessary.
572      */
573     private void updateProjectMetadata(StorageAsset targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
574                                        boolean fixChecksums, FileMetadata fileMetadata, String groupId,
575                                        String artifactId, String version, String packaging)
576             throws RepositoryMetadataException {
577         List<String> availableVersions = new ArrayList<>();
578         String latestVersion = version;
579
580         StorageAsset projectDir = targetPath.getParent();
581         StorageAsset projectMetadataFile = projectDir.resolve(MetadataTools.MAVEN_METADATA);
582
583         ArchivaRepositoryMetadata projectMetadata = getMetadata(projectMetadataFile);
584
585         if (projectMetadataFile.exists()) {
586             availableVersions = projectMetadata.getAvailableVersions();
587
588             Collections.sort(availableVersions, VersionComparator.getInstance());
589
590             if (!availableVersions.contains(version)) {
591                 availableVersions.add(version);
592             }
593
594             latestVersion = availableVersions.get(availableVersions.size() - 1);
595         } else {
596             availableVersions.add(version);
597
598             projectMetadata.setGroupId(groupId);
599             projectMetadata.setArtifactId(artifactId);
600         }
601
602         if (projectMetadata.getGroupId() == null) {
603             projectMetadata.setGroupId(groupId);
604         }
605
606         if (projectMetadata.getArtifactId() == null) {
607             projectMetadata.setArtifactId(artifactId);
608         }
609
610         projectMetadata.setLatestVersion(latestVersion);
611         projectMetadata.setLastUpdatedTimestamp(lastUpdatedTimestamp);
612         projectMetadata.setAvailableVersions(availableVersions);
613
614         if (!VersionUtil.isSnapshot(version)) {
615             projectMetadata.setReleasedVersion(latestVersion);
616         }
617
618         RepositoryMetadataWriter.write(projectMetadata, projectMetadataFile);
619
620         if (fixChecksums) {
621             fixChecksums(projectMetadataFile);
622         }
623     }
624
625     /**
626      * Update version level metadata for snapshot artifacts. If it does not exist, create the metadata and fix checksums
627      * if necessary.
628      */
629     private void updateVersionMetadata(ArchivaRepositoryMetadata metadata, StorageAsset metadataFile,
630                                        Date lastUpdatedTimestamp, String timestamp, int buildNumber,
631                                        boolean fixChecksums, FileMetadata fileMetadata, String groupId,
632                                        String artifactId, String version, String packaging)
633             throws RepositoryMetadataException {
634         if (!metadataFile.exists()) {
635             metadata.setGroupId(groupId);
636             metadata.setArtifactId(artifactId);
637             metadata.setVersion(version);
638         }
639
640         if (metadata.getSnapshotVersion() == null) {
641             metadata.setSnapshotVersion(new SnapshotVersion());
642         }
643
644         metadata.getSnapshotVersion().setBuildNumber(buildNumber);
645         metadata.getSnapshotVersion().setTimestamp(timestamp);
646         metadata.setLastUpdatedTimestamp(lastUpdatedTimestamp);
647
648         RepositoryMetadataWriter.write(metadata, metadataFile);
649
650         if (fixChecksums) {
651             fixChecksums(metadataFile);
652         }
653     }
654
655
656 }