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