You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DefaultFileUploadService.java 28KB

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