This project has retired. For details please refer to its Attic page.
Source code
001package org.apache.archiva.web.api;
002/*
003 * Licensed to the Apache Software Foundation (ASF) under one
004 * or more contributor license agreements.  See the NOTICE file
005 * distributed with this work for additional information
006 * regarding copyright ownership.  The ASF licenses this file
007 * to you under the Apache License, Version 2.0 (the
008 * "License"); you may not use this file except in compliance
009 * with the License.  You may obtain a copy of the License at
010 *
011 *   http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing,
014 * software distributed under the License is distributed on an
015 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
016 * KIND, either express or implied.  See the License for the
017 * specific language governing permissions and limitations
018 * under the License.
019 */
020
021import com.google.common.base.Predicate;
022import com.google.common.collect.Iterables;
023import org.apache.archiva.admin.model.RepositoryAdminException;
024import org.apache.archiva.admin.model.admin.ArchivaAdministration;
025import org.apache.archiva.admin.model.beans.ManagedRepository;
026import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
027import org.apache.archiva.checksum.ChecksumAlgorithm;
028import org.apache.archiva.checksum.ChecksumUtil;
029import org.apache.archiva.checksum.ChecksummedFile;
030import org.apache.archiva.common.utils.VersionComparator;
031import org.apache.archiva.common.utils.VersionUtil;
032import org.apache.archiva.configuration.ArchivaConfiguration;
033import org.apache.archiva.maven2.metadata.MavenMetadataReader;
034import org.apache.archiva.metadata.model.facets.AuditEvent;
035import org.apache.archiva.model.ArchivaRepositoryMetadata;
036import org.apache.archiva.model.ArtifactReference;
037import org.apache.archiva.model.SnapshotVersion;
038import org.apache.archiva.components.taskqueue.TaskQueueException;
039import org.apache.archiva.repository.RepositoryException;
040import org.apache.archiva.repository.RepositoryNotFoundException;
041import org.apache.archiva.repository.content.base.ArtifactUtil;
042import org.apache.archiva.repository.metadata.base.MetadataTools;
043import org.apache.archiva.repository.metadata.RepositoryMetadataException;
044import org.apache.archiva.repository.metadata.base.RepositoryMetadataWriter;
045import org.apache.archiva.repository.storage.StorageAsset;
046import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
047import org.apache.archiva.rest.services.AbstractRestService;
048import org.apache.archiva.scheduler.ArchivaTaskScheduler;
049import org.apache.archiva.scheduler.repository.model.RepositoryTask;
050import org.apache.archiva.web.model.FileMetadata;
051import org.apache.archiva.xml.XMLException;
052import org.apache.commons.io.FilenameUtils;
053import org.apache.commons.io.IOUtils;
054import org.apache.commons.lang3.BooleanUtils;
055import org.apache.commons.lang3.StringUtils;
056import org.apache.commons.lang3.SystemUtils;
057import org.apache.cxf.jaxrs.ext.multipart.Attachment;
058import org.apache.cxf.jaxrs.ext.multipart.MultipartBody;
059import org.apache.maven.model.Model;
060import org.apache.maven.model.io.xpp3.MavenXpp3Writer;
061import org.slf4j.Logger;
062import org.slf4j.LoggerFactory;
063import org.springframework.stereotype.Service;
064
065import javax.annotation.PostConstruct;
066import javax.inject.Inject;
067import javax.inject.Named;
068import javax.servlet.http.HttpServletRequest;
069import javax.servlet.http.HttpSession;
070import javax.ws.rs.core.Context;
071import javax.ws.rs.core.Response;
072import java.io.*;
073import java.net.URLDecoder;
074import java.nio.file.*;
075import java.text.DateFormat;
076import java.text.SimpleDateFormat;
077import java.util.*;
078import java.util.concurrent.CopyOnWriteArrayList;
079
080/**
081 *
082 * Service for uploading files to the repository.
083 *
084 * @author Olivier Lamy
085 * @author Martin Stockhammer
086 */
087@Service("fileUploadService#rest")
088public class DefaultFileUploadService
089        extends AbstractRestService
090        implements FileUploadService {
091    private Logger log = LoggerFactory.getLogger(getClass());
092
093    @Context
094    private HttpServletRequest httpServletRequest;
095
096    @Inject
097    private ManagedRepositoryAdmin managedRepositoryAdmin;
098
099    @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}