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.

Maven2RepositoryStorage.java 44KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994
  1. package org.apache.archiva.repository.maven.metadata.storage;
  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. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. import org.apache.archiva.checksum.ChecksumAlgorithm;
  20. import org.apache.archiva.checksum.ChecksummedFile;
  21. import org.apache.archiva.common.Try;
  22. import org.apache.archiva.common.utils.VersionUtil;
  23. import org.apache.archiva.filter.Filter;
  24. import org.apache.archiva.maven.metadata.MavenMetadataReader;
  25. import org.apache.archiva.metadata.model.ArtifactMetadata;
  26. import org.apache.archiva.metadata.model.ProjectMetadata;
  27. import org.apache.archiva.metadata.model.ProjectVersionMetadata;
  28. import org.apache.archiva.metadata.model.facets.RepositoryProblemFacet;
  29. import org.apache.archiva.metadata.repository.storage.ReadMetadataRequest;
  30. import org.apache.archiva.metadata.repository.storage.RelocationException;
  31. import org.apache.archiva.metadata.repository.storage.RepositoryPathTranslator;
  32. import org.apache.archiva.metadata.repository.storage.RepositoryStorage;
  33. import org.apache.archiva.metadata.repository.storage.RepositoryStorageMetadataInvalidException;
  34. import org.apache.archiva.metadata.repository.storage.RepositoryStorageMetadataNotFoundException;
  35. import org.apache.archiva.metadata.repository.storage.RepositoryStorageRuntimeException;
  36. import org.apache.archiva.model.ArchivaRepositoryMetadata;
  37. import org.apache.archiva.model.SnapshotVersion;
  38. import org.apache.archiva.policies.ProxyDownloadException;
  39. import org.apache.archiva.proxy.ProxyRegistry;
  40. import org.apache.archiva.maven.common.proxy.WagonFactory;
  41. import org.apache.archiva.proxy.model.NetworkProxy;
  42. import org.apache.archiva.proxy.model.ProxyConnector;
  43. import org.apache.archiva.proxy.model.RepositoryProxyHandler;
  44. import org.apache.archiva.repository.ManagedRepository;
  45. import org.apache.archiva.repository.ManagedRepositoryContent;
  46. import org.apache.archiva.repository.ReleaseScheme;
  47. import org.apache.archiva.repository.RemoteRepository;
  48. import org.apache.archiva.repository.RepositoryRegistry;
  49. import org.apache.archiva.repository.RepositoryType;
  50. import org.apache.archiva.repository.content.Artifact;
  51. import org.apache.archiva.repository.content.BaseRepositoryContentLayout;
  52. import org.apache.archiva.repository.content.ContentItem;
  53. import org.apache.archiva.repository.content.ItemSelector;
  54. import org.apache.archiva.repository.content.LayoutException;
  55. import org.apache.archiva.repository.content.base.ArchivaItemSelector;
  56. import org.apache.archiva.repository.maven.MavenSystemManager;
  57. import org.apache.archiva.repository.metadata.RepositoryMetadataException;
  58. import org.apache.archiva.repository.storage.StorageAsset;
  59. import org.apache.commons.lang3.ArrayUtils;
  60. import org.apache.commons.lang3.StringUtils;
  61. import org.apache.maven.model.CiManagement;
  62. import org.apache.maven.model.Dependency;
  63. import org.apache.maven.model.DistributionManagement;
  64. import org.apache.maven.model.IssueManagement;
  65. import org.apache.maven.model.License;
  66. import org.apache.maven.model.MailingList;
  67. import org.apache.maven.model.Model;
  68. import org.apache.maven.model.Organization;
  69. import org.apache.maven.model.Relocation;
  70. import org.apache.maven.model.Scm;
  71. import org.apache.maven.model.building.DefaultModelBuilderFactory;
  72. import org.apache.maven.model.building.DefaultModelBuildingRequest;
  73. import org.apache.maven.model.building.ModelBuilder;
  74. import org.apache.maven.model.building.ModelBuildingException;
  75. import org.apache.maven.model.building.ModelBuildingRequest;
  76. import org.apache.maven.model.building.ModelProblem;
  77. import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
  78. import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
  79. import org.slf4j.Logger;
  80. import org.slf4j.LoggerFactory;
  81. import org.springframework.context.ApplicationContext;
  82. import org.springframework.stereotype.Service;
  83. import javax.annotation.PostConstruct;
  84. import javax.inject.Inject;
  85. import javax.inject.Named;
  86. import java.io.FileNotFoundException;
  87. import java.io.IOException;
  88. import java.io.Reader;
  89. import java.nio.channels.Channels;
  90. import java.nio.charset.Charset;
  91. import java.nio.file.NoSuchFileException;
  92. import java.time.ZoneId;
  93. import java.time.ZonedDateTime;
  94. import java.util.ArrayList;
  95. import java.util.Arrays;
  96. import java.util.Collection;
  97. import java.util.Collections;
  98. import java.util.HashMap;
  99. import java.util.List;
  100. import java.util.Map;
  101. import java.util.Set;
  102. import java.util.function.Predicate;
  103. import java.util.stream.Collectors;
  104. // import java.io.FileNotFoundException;
  105. /**
  106. * <p>
  107. * Maven 2 repository format storage implementation. This class currently takes parameters to indicate the repository to
  108. * deal with rather than being instantiated per-repository.
  109. * FIXME: instantiate one per repository and allocate permanently from a factory (which can be obtained within the session).
  110. * </p>
  111. * <p>
  112. * The session is passed in as an argument to obtain any necessary resources, rather than the class being instantiated
  113. * within the session in the context of a single managed repository's resolution needs.
  114. * </p>
  115. */
  116. @Service("repositoryStorage#maven2")
  117. public class Maven2RepositoryStorage
  118. implements RepositoryStorage {
  119. private static final Logger log = LoggerFactory.getLogger(Maven2RepositoryStorage.class);
  120. private ModelBuilder builder;
  121. @Inject
  122. RepositoryRegistry repositoryRegistry;
  123. @Inject
  124. @Named( "metadataReader#maven" )
  125. MavenMetadataReader metadataReader;
  126. @Inject
  127. @Named("repositoryPathTranslator#maven2")
  128. private RepositoryPathTranslator pathTranslator;
  129. @Inject
  130. private WagonFactory wagonFactory;
  131. @Inject
  132. private ApplicationContext applicationContext;
  133. @Inject
  134. private ProxyRegistry proxyRegistry;
  135. @Inject
  136. private MavenSystemManager mavenSystemManager;
  137. private static final String METADATA_FILENAME_START = "maven-metadata";
  138. private static final String METADATA_FILENAME = METADATA_FILENAME_START + ".xml";
  139. // This array must be lexically sorted
  140. private static final String[] IGNORED_FILES = {METADATA_FILENAME, "resolver-status.properties"};
  141. private static final MavenXpp3Reader MAVEN_XPP_3_READER = new MavenXpp3Reader();
  142. @PostConstruct
  143. public void initialize() {
  144. builder = new DefaultModelBuilderFactory().newInstance();
  145. }
  146. @Override
  147. public ProjectMetadata readProjectMetadata(String repoId, String namespace, String projectId) {
  148. // TODO: could natively implement the "shared model" concept from the browse action to avoid needing it there?
  149. return null;
  150. }
  151. @Override
  152. public ProjectVersionMetadata readProjectVersionMetadata(ReadMetadataRequest readMetadataRequest)
  153. throws RepositoryStorageMetadataNotFoundException, RepositoryStorageMetadataInvalidException,
  154. RepositoryStorageRuntimeException {
  155. ManagedRepository managedRepository = repositoryRegistry.getManagedRepository(readMetadataRequest.getRepositoryId());
  156. boolean isReleases = managedRepository.getActiveReleaseSchemes().contains(ReleaseScheme.RELEASE);
  157. boolean isSnapshots = managedRepository.getActiveReleaseSchemes().contains(ReleaseScheme.SNAPSHOT);
  158. String artifactVersion = readMetadataRequest.getProjectVersion();
  159. // olamy: in case of browsing via the ui we can mix repos (parent of a SNAPSHOT can come from release repo)
  160. if (!readMetadataRequest.isBrowsingRequest()) {
  161. if (VersionUtil.isSnapshot(artifactVersion)) {
  162. // skygo trying to improve speed by honoring managed configuration MRM-1658
  163. if (isReleases && !isSnapshots) {
  164. throw new RepositoryStorageRuntimeException("lookforsnaponreleaseonly",
  165. "managed repo is configured for release only");
  166. }
  167. } else {
  168. if (!isReleases && isSnapshots) {
  169. throw new RepositoryStorageRuntimeException("lookforsreleaseonsneponly",
  170. "managed repo is configured for snapshot only");
  171. }
  172. }
  173. }
  174. StorageAsset basedir = managedRepository.getRoot();
  175. if (VersionUtil.isSnapshot(artifactVersion)) {
  176. StorageAsset metadataFile = pathTranslator.toFile(basedir, readMetadataRequest.getNamespace(),
  177. readMetadataRequest.getProjectId(), artifactVersion,
  178. METADATA_FILENAME);
  179. try {
  180. ArchivaRepositoryMetadata metadata = metadataReader.read(metadataFile);
  181. // re-adjust to timestamp if present, otherwise retain the original -SNAPSHOT filename
  182. SnapshotVersion snapshotVersion = metadata.getSnapshotVersion();
  183. if (snapshotVersion != null) {
  184. artifactVersion =
  185. artifactVersion.substring(0, artifactVersion.length() - 8); // remove SNAPSHOT from end
  186. artifactVersion =
  187. artifactVersion + snapshotVersion.getTimestamp() + "-" + snapshotVersion.getBuildNumber();
  188. }
  189. } catch ( RepositoryMetadataException e) {
  190. // unable to parse metadata - LOGGER it, and continue with the version as the original SNAPSHOT version
  191. log.warn("Invalid metadata: {} - {}", metadataFile, e.getMessage());
  192. }
  193. }
  194. // TODO: won't work well with some other layouts, might need to convert artifact parts to ID by path translator
  195. String id = readMetadataRequest.getProjectId() + "-" + artifactVersion + ".pom";
  196. StorageAsset file =
  197. pathTranslator.toFile(basedir, readMetadataRequest.getNamespace(), readMetadataRequest.getProjectId(),
  198. readMetadataRequest.getProjectVersion(), id);
  199. if (!file.exists()) {
  200. // metadata could not be resolved
  201. throw new RepositoryStorageMetadataNotFoundException(
  202. "The artifact's POM file '" + file.getPath() + "' was missing");
  203. }
  204. // TODO: this is a workaround until we can properly resolve using proxies as well - this doesn't cache
  205. // anything locally!
  206. List<RemoteRepository> remoteRepositories = new ArrayList<>();
  207. Map<String, NetworkProxy> networkProxies = new HashMap<>();
  208. Map<String, List<ProxyConnector>> proxyConnectorsMap = proxyRegistry.getProxyConnectorAsMap();
  209. List<ProxyConnector> proxyConnectors = proxyConnectorsMap.get(readMetadataRequest.getRepositoryId());
  210. if (proxyConnectors != null) {
  211. for (ProxyConnector proxyConnector : proxyConnectors) {
  212. RemoteRepository remoteRepoConfig =
  213. repositoryRegistry.getRemoteRepository(proxyConnector.getTargetRepository().getId());
  214. if (remoteRepoConfig != null) {
  215. remoteRepositories.add(remoteRepoConfig);
  216. NetworkProxy networkProxyConfig =
  217. proxyRegistry.getNetworkProxy(proxyConnector.getProxyId());
  218. if (networkProxyConfig != null) {
  219. // key/value: remote repo ID/proxy info
  220. networkProxies.put(proxyConnector.getTargetRepository().getId(), networkProxyConfig);
  221. }
  222. }
  223. }
  224. }
  225. // That's a browsing request so we can a mix of SNAPSHOT and release artifacts (especially with snapshots which
  226. // can have released parent pom
  227. if (readMetadataRequest.isBrowsingRequest()) {
  228. remoteRepositories.addAll(repositoryRegistry.getRemoteRepositories());
  229. }
  230. ModelBuildingRequest req =
  231. new DefaultModelBuildingRequest().setProcessPlugins(false).setPomFile(file.getFilePath().toFile()).setTwoPhaseBuilding(
  232. false).setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
  233. //MRM-1607. olamy this will resolve jdk profiles on the current running archiva jvm
  234. req.setSystemProperties(System.getProperties());
  235. // MRM-1411
  236. req.setModelResolver(
  237. new RepositoryModelResolver(managedRepository, pathTranslator, wagonFactory, remoteRepositories,
  238. networkProxies, managedRepository, mavenSystemManager, metadataReader));
  239. Model model;
  240. try {
  241. model = builder.build(req).getEffectiveModel();
  242. } catch (ModelBuildingException e) {
  243. String msg = "The artifact's POM file '" + file + "' was invalid: " + e.getMessage();
  244. List<ModelProblem> modelProblems = e.getProblems();
  245. for (ModelProblem problem : modelProblems) {
  246. // MRM-1411, related to MRM-1335
  247. // this means that the problem was that the parent wasn't resolved!
  248. // olamy really hackhish but fail with java profile so use error message
  249. // || ( StringUtils.startsWith( problem.getMessage(), "Failed to determine Java version for profile" ) )
  250. // but setTwoPhaseBuilding(true) fix that
  251. if (((problem.getException() instanceof FileNotFoundException
  252. || problem.getException() instanceof NoSuchFileException
  253. ) && e.getModelId() != null &&
  254. !e.getModelId().equals(problem.getModelId()))) {
  255. log.warn("The artifact's parent POM file '{}' cannot be resolved. "
  256. + "Using defaults for project version metadata..", file);
  257. ProjectVersionMetadata metadata = new ProjectVersionMetadata();
  258. metadata.setId(readMetadataRequest.getProjectVersion());
  259. MavenProjectFacet facet = new MavenProjectFacet();
  260. facet.setGroupId(readMetadataRequest.getNamespace());
  261. facet.setArtifactId(readMetadataRequest.getProjectId());
  262. facet.setPackaging("jar");
  263. metadata.addFacet(facet);
  264. String errMsg =
  265. "Error in resolving artifact's parent POM file. " + (problem.getException() == null
  266. ? problem.getMessage()
  267. : problem.getException().getMessage());
  268. RepositoryProblemFacet repoProblemFacet = new RepositoryProblemFacet();
  269. repoProblemFacet.setRepositoryId(readMetadataRequest.getRepositoryId());
  270. repoProblemFacet.setId(readMetadataRequest.getRepositoryId());
  271. repoProblemFacet.setMessage(errMsg);
  272. repoProblemFacet.setProblem(errMsg);
  273. repoProblemFacet.setProject(readMetadataRequest.getProjectId());
  274. repoProblemFacet.setVersion(readMetadataRequest.getProjectVersion());
  275. repoProblemFacet.setNamespace(readMetadataRequest.getNamespace());
  276. metadata.addFacet(repoProblemFacet);
  277. return metadata;
  278. }
  279. }
  280. throw new RepositoryStorageMetadataInvalidException("invalid-pom", msg, e);
  281. }
  282. // Check if the POM is in the correct location
  283. boolean correctGroupId = readMetadataRequest.getNamespace().equals(model.getGroupId());
  284. boolean correctArtifactId = readMetadataRequest.getProjectId().equals(model.getArtifactId());
  285. boolean correctVersion = readMetadataRequest.getProjectVersion().equals(model.getVersion());
  286. if (!correctGroupId || !correctArtifactId || !correctVersion) {
  287. StringBuilder message = new StringBuilder("Incorrect POM coordinates in '" + file + "':");
  288. if (!correctGroupId) {
  289. message.append("\nIncorrect group ID: ").append(model.getGroupId());
  290. }
  291. if (!correctArtifactId) {
  292. message.append("\nIncorrect artifact ID: ").append(model.getArtifactId());
  293. }
  294. if (!correctVersion) {
  295. message.append("\nIncorrect version: ").append(model.getVersion());
  296. }
  297. throw new RepositoryStorageMetadataInvalidException("mislocated-pom", message.toString());
  298. }
  299. ProjectVersionMetadata metadata = new ProjectVersionMetadata();
  300. metadata.setCiManagement(convertCiManagement(model.getCiManagement()));
  301. metadata.setDescription(model.getDescription());
  302. metadata.setId(readMetadataRequest.getProjectVersion());
  303. metadata.setIssueManagement(convertIssueManagement(model.getIssueManagement()));
  304. metadata.setLicenses(convertLicenses(model.getLicenses()));
  305. metadata.setMailingLists(convertMailingLists(model.getMailingLists()));
  306. metadata.setDependencies(convertDependencies(model.getDependencies()));
  307. metadata.setName(model.getName());
  308. metadata.setOrganization(convertOrganization(model.getOrganization()));
  309. metadata.setScm(convertScm(model.getScm()));
  310. metadata.setUrl(model.getUrl());
  311. metadata.setProperties( new HashMap<String, String>( (Map) model.getProperties( ) ) );
  312. MavenProjectFacet facet = new MavenProjectFacet();
  313. facet.setGroupId(model.getGroupId() != null ? model.getGroupId() : model.getParent().getGroupId());
  314. facet.setArtifactId(model.getArtifactId());
  315. facet.setPackaging(model.getPackaging());
  316. if (model.getParent() != null) {
  317. MavenProjectParent parent = new MavenProjectParent();
  318. parent.setGroupId(model.getParent().getGroupId());
  319. parent.setArtifactId(model.getParent().getArtifactId());
  320. parent.setVersion(model.getParent().getVersion());
  321. facet.setParent(parent);
  322. }
  323. metadata.addFacet(facet);
  324. return metadata;
  325. }
  326. public void setWagonFactory(WagonFactory wagonFactory) {
  327. this.wagonFactory = wagonFactory;
  328. }
  329. private List<org.apache.archiva.metadata.model.Dependency> convertDependencies(List<Dependency> dependencies) {
  330. List<org.apache.archiva.metadata.model.Dependency> l = new ArrayList<>();
  331. for (Dependency dependency : dependencies) {
  332. org.apache.archiva.metadata.model.Dependency newDependency =
  333. new org.apache.archiva.metadata.model.Dependency();
  334. newDependency.setArtifactId(dependency.getArtifactId());
  335. newDependency.setClassifier(dependency.getClassifier());
  336. newDependency.setNamespace(dependency.getGroupId());
  337. newDependency.setOptional(dependency.isOptional());
  338. newDependency.setScope(dependency.getScope());
  339. newDependency.setSystemPath(dependency.getSystemPath());
  340. newDependency.setType(dependency.getType());
  341. newDependency.setVersion(dependency.getVersion());
  342. l.add(newDependency);
  343. }
  344. return l;
  345. }
  346. private org.apache.archiva.metadata.model.Scm convertScm(Scm scm) {
  347. org.apache.archiva.metadata.model.Scm newScm = null;
  348. if (scm != null) {
  349. newScm = new org.apache.archiva.metadata.model.Scm();
  350. newScm.setConnection(scm.getConnection());
  351. newScm.setDeveloperConnection(scm.getDeveloperConnection());
  352. newScm.setUrl(scm.getUrl());
  353. }
  354. return newScm;
  355. }
  356. private org.apache.archiva.metadata.model.Organization convertOrganization(Organization organization) {
  357. org.apache.archiva.metadata.model.Organization org = null;
  358. if (organization != null) {
  359. org = new org.apache.archiva.metadata.model.Organization();
  360. org.setName(organization.getName());
  361. org.setUrl(organization.getUrl());
  362. }
  363. return org;
  364. }
  365. private List<org.apache.archiva.metadata.model.License> convertLicenses(List<License> licenses) {
  366. List<org.apache.archiva.metadata.model.License> l = new ArrayList<>();
  367. for (License license : licenses) {
  368. org.apache.archiva.metadata.model.License newLicense = new org.apache.archiva.metadata.model.License();
  369. newLicense.setName(license.getName());
  370. newLicense.setUrl(license.getUrl());
  371. l.add(newLicense);
  372. }
  373. return l;
  374. }
  375. private List<org.apache.archiva.metadata.model.MailingList> convertMailingLists(List<MailingList> mailingLists) {
  376. List<org.apache.archiva.metadata.model.MailingList> l = new ArrayList<>();
  377. for (MailingList mailingList : mailingLists) {
  378. org.apache.archiva.metadata.model.MailingList newMailingList =
  379. new org.apache.archiva.metadata.model.MailingList();
  380. newMailingList.setName(mailingList.getName());
  381. newMailingList.setMainArchiveUrl(mailingList.getArchive());
  382. newMailingList.setPostAddress(mailingList.getPost());
  383. newMailingList.setSubscribeAddress(mailingList.getSubscribe());
  384. newMailingList.setUnsubscribeAddress(mailingList.getUnsubscribe());
  385. newMailingList.setOtherArchives(mailingList.getOtherArchives());
  386. l.add(newMailingList);
  387. }
  388. return l;
  389. }
  390. private org.apache.archiva.metadata.model.IssueManagement convertIssueManagement(IssueManagement issueManagement) {
  391. org.apache.archiva.metadata.model.IssueManagement im = null;
  392. if (issueManagement != null) {
  393. im = new org.apache.archiva.metadata.model.IssueManagement();
  394. im.setSystem(issueManagement.getSystem());
  395. im.setUrl(issueManagement.getUrl());
  396. }
  397. return im;
  398. }
  399. private org.apache.archiva.metadata.model.CiManagement convertCiManagement(CiManagement ciManagement) {
  400. org.apache.archiva.metadata.model.CiManagement ci = null;
  401. if (ciManagement != null) {
  402. ci = new org.apache.archiva.metadata.model.CiManagement();
  403. ci.setSystem(ciManagement.getSystem());
  404. ci.setUrl(ciManagement.getUrl());
  405. }
  406. return ci;
  407. }
  408. @Override
  409. public Collection<String> listRootNamespaces(String repoId, Filter<String> filter)
  410. throws RepositoryStorageRuntimeException {
  411. StorageAsset dir = getRepositoryBasedir(repoId);
  412. return getSortedFiles(dir, filter);
  413. }
  414. private static Collection<String> getSortedFiles(StorageAsset dir, Filter<String> filter) {
  415. final Predicate<StorageAsset> dFilter = new DirectoryFilter(filter);
  416. return dir.list().stream().filter(f -> f.isContainer())
  417. .filter(dFilter)
  418. .map(path -> path.getName().toString())
  419. .sorted().collect(Collectors.toList());
  420. }
  421. private StorageAsset getRepositoryBasedir(String repoId)
  422. throws RepositoryStorageRuntimeException {
  423. ManagedRepository repository = repositoryRegistry.getManagedRepository(repoId);
  424. return repository.getRoot();
  425. }
  426. @Override
  427. public Collection<String> listNamespaces(String repoId, String namespace, Filter<String> filter)
  428. throws RepositoryStorageRuntimeException {
  429. StorageAsset dir = pathTranslator.toFile(getRepositoryBasedir(repoId), namespace);
  430. if (!(dir.exists()) && !dir.isContainer()) {
  431. return Collections.emptyList();
  432. }
  433. // scan all the directories which are potential namespaces. Any directories known to be projects are excluded
  434. Predicate<StorageAsset> dFilter = new DirectoryFilter(filter);
  435. return dir.list().stream().filter(dFilter).filter(path -> !isProject(path, filter)).map(path -> path.getName().toString())
  436. .sorted().collect(Collectors.toList());
  437. }
  438. @Override
  439. public Collection<String> listProjects(String repoId, String namespace, Filter<String> filter)
  440. throws RepositoryStorageRuntimeException {
  441. StorageAsset dir = pathTranslator.toFile(getRepositoryBasedir(repoId), namespace);
  442. if (!(dir.exists() && dir.isContainer())) {
  443. return Collections.emptyList();
  444. }
  445. // scan all directories in the namespace, and only include those that are known to be projects
  446. final Predicate<StorageAsset> dFilter = new DirectoryFilter(filter);
  447. return dir.list().stream().filter(dFilter).filter(path -> isProject(path, filter)).map(path -> path.getName().toString())
  448. .sorted().collect(Collectors.toList());
  449. }
  450. @Override
  451. public Collection<String> listProjectVersions(String repoId, String namespace, String projectId,
  452. Filter<String> filter)
  453. throws RepositoryStorageRuntimeException {
  454. StorageAsset dir = pathTranslator.toFile(getRepositoryBasedir(repoId), namespace, projectId);
  455. if (!(dir.exists() && dir.isContainer())) {
  456. return Collections.emptyList();
  457. }
  458. // all directories in a project directory can be considered a version
  459. return getSortedFiles(dir, filter);
  460. }
  461. @Override
  462. public Collection<ArtifactMetadata> readArtifactsMetadata(ReadMetadataRequest readMetadataRequest)
  463. throws RepositoryStorageRuntimeException {
  464. StorageAsset dir = pathTranslator.toFile(getRepositoryBasedir(readMetadataRequest.getRepositoryId()),
  465. readMetadataRequest.getNamespace(), readMetadataRequest.getProjectId(),
  466. readMetadataRequest.getProjectVersion());
  467. if (!(dir.exists() && dir.isContainer())) {
  468. return Collections.emptyList();
  469. }
  470. // all files that are not metadata and not a checksum / signature are considered artifacts
  471. final Predicate<StorageAsset> dFilter = new ArtifactDirectoryFilter(readMetadataRequest.getFilter());
  472. // Returns a map TRUE -> (success values), FALSE -> (Exceptions)
  473. Map<Boolean, List<Try<ArtifactMetadata>>> result = dir.list().stream().filter(dFilter).map(path -> {
  474. try {
  475. return Try.success(getArtifactFromFile(readMetadataRequest.getRepositoryId(), readMetadataRequest.getNamespace(),
  476. readMetadataRequest.getProjectId(), readMetadataRequest.getProjectVersion(),
  477. path));
  478. } catch (Exception e) {
  479. log.debug("Could not create metadata for {}: {}", path, e.getMessage(), e);
  480. return Try.<ArtifactMetadata>failure(e);
  481. }
  482. }
  483. ).collect(Collectors.groupingBy(Try::isSuccess));
  484. if (result.containsKey(Boolean.FALSE) && result.get(Boolean.FALSE).size() > 0 && (!result.containsKey(Boolean.TRUE) || result.get(Boolean.TRUE).size() == 0)) {
  485. log.error("Could not get artifact metadata. Directory: {}. Number of errors {}.", dir, result.get(Boolean.FALSE).size());
  486. Try<ArtifactMetadata> failure = result.get(Boolean.FALSE).get(0);
  487. log.error("Sample exception {}", failure.getError().getMessage(), failure.getError());
  488. throw new RepositoryStorageRuntimeException(readMetadataRequest.getRepositoryId(), "Could not retrieve metadata of the files");
  489. } else {
  490. if (!result.containsKey(Boolean.TRUE) || result.get(Boolean.TRUE) == null) {
  491. return Collections.emptyList();
  492. }
  493. return result.get(Boolean.TRUE).stream().map(tr -> tr.get()).collect(Collectors.toList());
  494. }
  495. }
  496. @Override
  497. public ArtifactMetadata readArtifactMetadataFromPath(String repoId, String path)
  498. throws RepositoryStorageRuntimeException {
  499. ArtifactMetadata metadata = pathTranslator.getArtifactForPath(repoId, path);
  500. try {
  501. populateArtifactMetadataFromFile(metadata, getRepositoryBasedir(repoId).resolve(path));
  502. } catch (IOException e) {
  503. throw new RepositoryStorageRuntimeException(repoId, "Error during metadata retrieval of " + path + " :" + e.getMessage(), e);
  504. }
  505. return metadata;
  506. }
  507. private ArtifactMetadata getArtifactFromFile(String repoId, String namespace, String projectId,
  508. String projectVersion, StorageAsset file) throws IOException {
  509. ArtifactMetadata metadata =
  510. pathTranslator.getArtifactFromId(repoId, namespace, projectId, projectVersion, file.getName());
  511. populateArtifactMetadataFromFile(metadata, file);
  512. return metadata;
  513. }
  514. @Override
  515. public ItemSelector applyServerSideRelocation(ManagedRepository managedRepository, ItemSelector artifactSelector)
  516. throws ProxyDownloadException {
  517. if ("pom".equals(artifactSelector.getType())) {
  518. return artifactSelector;
  519. }
  520. // Build the artifact POM reference
  521. BaseRepositoryContentLayout layout;
  522. try
  523. {
  524. layout = managedRepository.getContent( ).getLayout( BaseRepositoryContentLayout.class );
  525. }
  526. catch ( LayoutException e )
  527. {
  528. throw new ProxyDownloadException( "Could not set layout " + e.getMessage( ), new HashMap<>( ) );
  529. }
  530. RepositoryType repositoryType = managedRepository.getType();
  531. if (!proxyRegistry.hasHandler(repositoryType)) {
  532. throw new ProxyDownloadException("No proxy handler found for repository type " + repositoryType, new HashMap<>());
  533. }
  534. ItemSelector selector = ArchivaItemSelector.builder( )
  535. .withNamespace( artifactSelector.getNamespace( ) )
  536. .withProjectId( artifactSelector.getArtifactId( ) )
  537. .withArtifactId( artifactSelector.getArtifactId( ) )
  538. .withVersion( artifactSelector.getVersion( ) )
  539. .withArtifactVersion( artifactSelector.getVersion( ) )
  540. .withType( "pom" ).build( );
  541. Artifact pom = layout.getArtifact( selector );
  542. RepositoryProxyHandler proxyHandler = proxyRegistry.getHandler(repositoryType).get(0);
  543. // Get the artifact POM from proxied repositories if needed
  544. proxyHandler.fetchFromProxies(managedRepository, pom);
  545. // Open and read the POM from the managed repo
  546. if (!pom.exists()) {
  547. return artifactSelector;
  548. }
  549. try {
  550. // MavenXpp3Reader leaves the file open, so we need to close it ourselves.
  551. Model model;
  552. try (Reader reader = Channels.newReader(pom.getAsset().getReadChannel(), Charset.defaultCharset().name())) {
  553. model = MAVEN_XPP_3_READER.read(reader);
  554. }
  555. DistributionManagement dist = model.getDistributionManagement();
  556. if (dist != null) {
  557. Relocation relocation = dist.getRelocation();
  558. if (relocation != null) {
  559. ArchivaItemSelector.Builder relocatedBuilder = ArchivaItemSelector.builder( );
  560. // artifact is relocated : update the repositoryPath
  561. if (relocation.getGroupId() != null) {
  562. relocatedBuilder.withNamespace( relocation.getGroupId( ) );
  563. } else {
  564. relocatedBuilder.withNamespace( artifactSelector.getNamespace( ) );
  565. }
  566. if (relocation.getArtifactId() != null) {
  567. relocatedBuilder.withArtifactId( relocation.getArtifactId( ) );
  568. } else {
  569. relocatedBuilder.withArtifactId( artifactSelector.getArtifactId( ) );
  570. }
  571. if (relocation.getVersion() != null)
  572. {
  573. relocatedBuilder.withVersion( relocation.getVersion( ) );
  574. } else {
  575. relocatedBuilder.withVersion( artifactSelector.getVersion( ) );
  576. }
  577. return relocatedBuilder.withArtifactVersion( artifactSelector.getArtifactVersion( ) )
  578. .withClassifier( artifactSelector.getClassifier( ) )
  579. .withType( artifactSelector.getType( ) )
  580. .withProjectId( artifactSelector.getProjectId( ) )
  581. .withExtension( artifactSelector.getExtension( ) )
  582. .build( );
  583. }
  584. }
  585. } catch (IOException e) {
  586. // Unable to read POM : ignore.
  587. } catch (XmlPullParserException e) {
  588. // Invalid POM : ignore
  589. }
  590. return artifactSelector;
  591. }
  592. @Override
  593. public String getFilePath(String requestPath, org.apache.archiva.repository.ManagedRepository managedRepository) {
  594. // managedRepository can be null
  595. // extract artifact reference from url
  596. // groupId:artifactId:version:packaging:classifier
  597. //org/apache/archiva/archiva-checksum/1.4-M4-SNAPSHOT/archiva-checksum-1.4-M4-SNAPSHOT.jar
  598. String logicalResource = null;
  599. String requestPathInfo = StringUtils.defaultString(requestPath);
  600. //remove prefix ie /repository/blah becomes /blah
  601. requestPathInfo = removePrefix(requestPathInfo);
  602. // Remove prefixing slash as the repository id doesn't contain it;
  603. if (requestPathInfo.startsWith("/")) {
  604. requestPathInfo = requestPathInfo.substring(1);
  605. }
  606. int slash = requestPathInfo.indexOf('/');
  607. if (slash > 0) {
  608. logicalResource = requestPathInfo.substring(slash);
  609. if (logicalResource.endsWith("/..")) {
  610. logicalResource += "/";
  611. }
  612. if (logicalResource != null && logicalResource.startsWith("//")) {
  613. logicalResource = logicalResource.substring(1);
  614. }
  615. if (logicalResource == null) {
  616. logicalResource = "/";
  617. }
  618. } else {
  619. logicalResource = "/";
  620. }
  621. return logicalResource;
  622. }
  623. @Override
  624. public String getFilePathWithVersion(final String requestPath, ManagedRepositoryContent managedRepositoryContent)
  625. throws RelocationException
  626. {
  627. if (StringUtils.endsWith(requestPath, METADATA_FILENAME)) {
  628. return getFilePath(requestPath, managedRepositoryContent.getRepository());
  629. }
  630. String filePath = getFilePath(requestPath, managedRepositoryContent.getRepository());
  631. Artifact artifact;
  632. try
  633. {
  634. ContentItem item = managedRepositoryContent.toItem( filePath );
  635. artifact = managedRepositoryContent.getLayout( BaseRepositoryContentLayout.class ).adaptItem( Artifact.class, item );
  636. }
  637. catch ( LayoutException e )
  638. {
  639. return filePath;
  640. }
  641. if (VersionUtil.isGenericSnapshot(artifact.getArtifactVersion())) {
  642. // read maven metadata to get last timestamp
  643. StorageAsset metadataDir = managedRepositoryContent.getRepository().getAsset(filePath).getParent();
  644. if (!metadataDir.exists()) {
  645. return filePath;
  646. }
  647. StorageAsset metadataFile = metadataDir.resolve(METADATA_FILENAME);
  648. if (!metadataFile.exists()) {
  649. return filePath;
  650. }
  651. ArchivaRepositoryMetadata archivaRepositoryMetadata = null;
  652. try
  653. {
  654. archivaRepositoryMetadata = metadataReader.read(metadataFile);
  655. }
  656. catch ( RepositoryMetadataException e )
  657. {
  658. log.error( "Could not read metadata {}", e.getMessage( ), e );
  659. return filePath;
  660. }
  661. int buildNumber = archivaRepositoryMetadata.getSnapshotVersion().getBuildNumber();
  662. String timestamp = archivaRepositoryMetadata.getSnapshotVersion().getTimestamp();
  663. // MRM-1846
  664. if (buildNumber < 1 && timestamp == null) {
  665. return filePath;
  666. }
  667. // org/apache/archiva/archiva-checksum/1.4-M4-SNAPSHOT/archiva-checksum-1.4-M4-SNAPSHOT.jar
  668. // -> archiva-checksum-1.4-M4-20130425.081822-1.jar
  669. filePath = StringUtils.replace(filePath, //
  670. artifact.getId() //
  671. + "-" + artifact.getVersion().getId(), //
  672. artifact.getId() //
  673. + "-" + StringUtils.remove(artifact.getArtifactVersion(),
  674. "-" + VersionUtil.SNAPSHOT) //
  675. + "-" + timestamp //
  676. + "-" + buildNumber);
  677. throw new RelocationException("/repository/" + managedRepositoryContent.getRepository().getId() +
  678. (StringUtils.startsWith(filePath, "/") ? "" : "/") + filePath,
  679. RelocationException.RelocationType.TEMPORARY);
  680. }
  681. return filePath;
  682. }
  683. //-----------------------------
  684. // internal
  685. //-----------------------------
  686. /**
  687. * FIXME remove
  688. *
  689. * @param href
  690. * @return
  691. */
  692. private static String removePrefix(final String href) {
  693. String[] parts = StringUtils.split(href, '/');
  694. parts = (String[]) ArrayUtils.subarray(parts, 1, parts.length);
  695. if (parts == null || parts.length == 0) {
  696. return "/";
  697. }
  698. String joinedString = StringUtils.join(parts, '/');
  699. if (href.endsWith("/")) {
  700. joinedString = joinedString + "/";
  701. }
  702. return joinedString;
  703. }
  704. private static void populateArtifactMetadataFromFile(ArtifactMetadata metadata, StorageAsset file) throws IOException {
  705. metadata.setWhenGathered(ZonedDateTime.now(ZoneId.of("GMT")));
  706. metadata.setFileLastModified(file.getModificationTime().toEpochMilli());
  707. ChecksummedFile checksummedFile = new ChecksummedFile(file.getFilePath());
  708. try {
  709. metadata.setMd5(checksummedFile.calculateChecksum(ChecksumAlgorithm.MD5));
  710. } catch (IOException e) {
  711. log.error("Unable to checksum file {}: {},MD5", file, e.getMessage());
  712. }
  713. try {
  714. metadata.setSha1(checksummedFile.calculateChecksum(ChecksumAlgorithm.SHA1));
  715. } catch (IOException e) {
  716. log.error("Unable to checksum file {}: {},SHA1", file, e.getMessage());
  717. }
  718. metadata.setSize(file.getSize());
  719. }
  720. private boolean isProject(StorageAsset dir, Filter<String> filter) {
  721. // scan directories for a valid project version subdirectory, meaning this must be a project directory
  722. final Predicate<StorageAsset> dFilter = new DirectoryFilter(filter);
  723. boolean projFound = dir.list().stream().filter(dFilter)
  724. .anyMatch(path -> isProjectVersion(path));
  725. if (projFound) {
  726. return true;
  727. }
  728. // if a metadata file is present, check if this is the "artifactId" directory, marking it as a project
  729. ArchivaRepositoryMetadata metadata = readMetadata(dir);
  730. if (metadata != null && dir.getName().toString().equals(metadata.getArtifactId())) {
  731. return true;
  732. }
  733. return false;
  734. }
  735. private boolean isProjectVersion(StorageAsset dir) {
  736. final String artifactId = dir.getParent().getName();
  737. final String projectVersion = dir.getName();
  738. // check if there is a POM artifact file to ensure it is a version directory
  739. Predicate<StorageAsset> filter;
  740. if (VersionUtil.isSnapshot(projectVersion)) {
  741. filter = new PomFilenameFilter(artifactId, projectVersion);
  742. } else {
  743. final String pomFile = artifactId + "-" + projectVersion + ".pom";
  744. filter = new PomFileFilter(pomFile);
  745. }
  746. if (dir.list().stream().filter(f -> !f.isContainer()).anyMatch(filter)) {
  747. return true;
  748. }
  749. // if a metadata file is present, check if this is the "version" directory, marking it as a project version
  750. ArchivaRepositoryMetadata metadata = readMetadata(dir);
  751. if (metadata != null && projectVersion.equals(metadata.getVersion())) {
  752. return true;
  753. }
  754. return false;
  755. }
  756. private ArchivaRepositoryMetadata readMetadata(StorageAsset directory) {
  757. ArchivaRepositoryMetadata metadata = null;
  758. StorageAsset metadataFile = directory.resolve(METADATA_FILENAME);
  759. if (metadataFile.exists()) {
  760. try {
  761. metadata = metadataReader.read(metadataFile);
  762. } catch ( RepositoryMetadataException e )
  763. {
  764. // Ignore missing or invalid metadata
  765. }
  766. }
  767. return metadata;
  768. }
  769. private static class DirectoryFilter
  770. implements Predicate<StorageAsset> {
  771. private final Filter<String> filter;
  772. public DirectoryFilter(Filter<String> filter) {
  773. this.filter = filter;
  774. }
  775. @Override
  776. public boolean test(StorageAsset dir) {
  777. final String name = dir.getName();
  778. if (!filter.accept(name)) {
  779. return false;
  780. } else if (name.startsWith(".")) {
  781. return false;
  782. } else if (!dir.isContainer()) {
  783. return false;
  784. }
  785. return true;
  786. }
  787. }
  788. private static class ArtifactDirectoryFilter
  789. implements Predicate<StorageAsset> {
  790. private final Filter<String> filter;
  791. private ArtifactDirectoryFilter(Filter<String> filter) {
  792. this.filter = filter;
  793. }
  794. @Override
  795. public boolean test(StorageAsset file) {
  796. final Set<String> checksumExts = ChecksumAlgorithm.getAllExtensions();
  797. final String path = file.getPath();
  798. final String name = file.getName();
  799. final String extension = StringUtils.substringAfterLast(name, ".").toLowerCase();
  800. // TODO compare to logic in maven-repository-layer
  801. if (file.isContainer()) {
  802. return false;
  803. } else if (!filter.accept(name)) {
  804. return false;
  805. } else if (name.startsWith(".") || path.contains("/.") ) {
  806. return false;
  807. } else if (checksumExts.contains(extension)) {
  808. return false;
  809. } else if (Arrays.binarySearch(IGNORED_FILES, name) >= 0) {
  810. return false;
  811. }
  812. // some files from remote repositories can have name like maven-metadata-archiva-vm-all-public.xml
  813. else if (StringUtils.startsWith(name, METADATA_FILENAME_START) && StringUtils.endsWith(name, ".xml")) {
  814. return false;
  815. }
  816. return true;
  817. }
  818. }
  819. private static final class PomFilenameFilter
  820. implements Predicate<StorageAsset> {
  821. private final String artifactId, projectVersion;
  822. private PomFilenameFilter(String artifactId, String projectVersion) {
  823. this.artifactId = artifactId;
  824. this.projectVersion = projectVersion;
  825. }
  826. @Override
  827. public boolean test(StorageAsset dir) {
  828. final String name = dir.getName();
  829. if (name.startsWith(artifactId + "-") && name.endsWith(".pom")) {
  830. String v = name.substring(artifactId.length() + 1, name.length() - 4);
  831. v = VersionUtil.getBaseVersion(v);
  832. if (v.equals(projectVersion)) {
  833. return true;
  834. }
  835. }
  836. return false;
  837. }
  838. }
  839. private static class PomFileFilter
  840. implements Predicate<StorageAsset> {
  841. private final String pomFile;
  842. private PomFileFilter(String pomFile) {
  843. this.pomFile = pomFile;
  844. }
  845. @Override
  846. public boolean test(StorageAsset dir) {
  847. return pomFile.equals(dir.getName());
  848. }
  849. }
  850. }