1 package org.apache.archiva.repository.maven.metadata.storage;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
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
21 import org.apache.archiva.checksum.ChecksumAlgorithm;
22 import org.apache.archiva.checksum.ChecksummedFile;
23 import org.apache.archiva.common.Try;
24 import org.apache.archiva.common.utils.VersionUtil;
25 import org.apache.archiva.filter.Filter;
26 import org.apache.archiva.metadata.maven.MavenMetadataReader;
27 import org.apache.archiva.metadata.model.ArtifactMetadata;
28 import org.apache.archiva.metadata.model.ProjectMetadata;
29 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
30 import org.apache.archiva.metadata.model.facets.RepositoryProblemFacet;
31 import org.apache.archiva.metadata.repository.storage.*;
32 import org.apache.archiva.model.ArchivaRepositoryMetadata;
33 import org.apache.archiva.model.SnapshotVersion;
34 import org.apache.archiva.policies.ProxyDownloadException;
35 import org.apache.archiva.proxy.ProxyRegistry;
36 import org.apache.archiva.proxy.maven.WagonFactory;
37 import org.apache.archiva.proxy.model.NetworkProxy;
38 import org.apache.archiva.proxy.model.ProxyConnector;
39 import org.apache.archiva.proxy.model.RepositoryProxyHandler;
40 import org.apache.archiva.repository.*;
41 import org.apache.archiva.repository.content.Artifact;
42 import org.apache.archiva.repository.content.ContentItem;
43 import org.apache.archiva.repository.content.ItemSelector;
44 import org.apache.archiva.repository.content.PathParser;
45 import org.apache.archiva.repository.content.base.ArchivaItemSelector;
46 import org.apache.archiva.repository.maven.MavenSystemManager;
47 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
48 import org.apache.archiva.repository.storage.StorageAsset;
49 import org.apache.commons.lang3.ArrayUtils;
50 import org.apache.commons.lang3.StringUtils;
51 import org.apache.maven.model.*;
52 import org.apache.maven.model.building.*;
53 import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
54 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
55 import org.slf4j.Logger;
56 import org.slf4j.LoggerFactory;
57 import org.springframework.context.ApplicationContext;
58 import org.springframework.stereotype.Service;
60 import javax.annotation.PostConstruct;
61 import javax.inject.Inject;
62 import javax.inject.Named;
63 import java.io.FileNotFoundException;
64 import java.io.IOException;
65 import java.io.Reader;
66 import java.nio.channels.Channels;
67 import java.nio.charset.Charset;
68 import java.nio.file.NoSuchFileException;
69 import java.time.ZoneId;
70 import java.time.ZonedDateTime;
72 import java.util.function.Predicate;
73 import java.util.stream.Collectors;
75 // import java.io.FileNotFoundException;
79 * Maven 2 repository format storage implementation. This class currently takes parameters to indicate the repository to
80 * deal with rather than being instantiated per-repository.
81 * FIXME: instantiate one per repository and allocate permanently from a factory (which can be obtained within the session).
84 * The session is passed in as an argument to obtain any necessary resources, rather than the class being instantiated
85 * within the session in the context of a single managed repository's resolution needs.
88 @Service("repositoryStorage#maven2")
89 public class Maven2RepositoryStorage
90 implements RepositoryStorage {
92 private static final Logger log = LoggerFactory.getLogger(Maven2RepositoryStorage.class);
94 private ModelBuilder builder;
97 RepositoryRegistry repositoryRegistry;
100 @Named( "metadataReader#maven" )
101 MavenMetadataReader metadataReader;
104 @Named("repositoryPathTranslator#maven2")
105 private RepositoryPathTranslator pathTranslator;
108 private WagonFactory wagonFactory;
111 private ApplicationContext applicationContext;
114 @Named("pathParser#default")
115 private PathParser pathParser;
118 private ProxyRegistry proxyRegistry;
121 private MavenSystemManager mavenSystemManager;
123 private static final String METADATA_FILENAME_START = "maven-metadata";
125 private static final String METADATA_FILENAME = METADATA_FILENAME_START + ".xml";
127 // This array must be lexically sorted
128 private static final String[] IGNORED_FILES = {METADATA_FILENAME, "resolver-status.properties"};
130 private static final MavenXpp3Reader MAVEN_XPP_3_READER = new MavenXpp3Reader();
134 public void initialize() {
135 builder = new DefaultModelBuilderFactory().newInstance();
140 public ProjectMetadata readProjectMetadata(String repoId, String namespace, String projectId) {
141 // TODO: could natively implement the "shared model" concept from the browse action to avoid needing it there?
146 public ProjectVersionMetadata readProjectVersionMetadata(ReadMetadataRequest readMetadataRequest)
147 throws RepositoryStorageMetadataNotFoundException, RepositoryStorageMetadataInvalidException,
148 RepositoryStorageRuntimeException {
150 ManagedRepository managedRepository = repositoryRegistry.getManagedRepository(readMetadataRequest.getRepositoryId());
151 boolean isReleases = managedRepository.getActiveReleaseSchemes().contains(ReleaseScheme.RELEASE);
152 boolean isSnapshots = managedRepository.getActiveReleaseSchemes().contains(ReleaseScheme.SNAPSHOT);
153 String artifactVersion = readMetadataRequest.getProjectVersion();
154 // olamy: in case of browsing via the ui we can mix repos (parent of a SNAPSHOT can come from release repo)
155 if (!readMetadataRequest.isBrowsingRequest()) {
156 if (VersionUtil.isSnapshot(artifactVersion)) {
157 // skygo trying to improve speed by honoring managed configuration MRM-1658
158 if (isReleases && !isSnapshots) {
159 throw new RepositoryStorageRuntimeException("lookforsnaponreleaseonly",
160 "managed repo is configured for release only");
163 if (!isReleases && isSnapshots) {
164 throw new RepositoryStorageRuntimeException("lookforsreleaseonsneponly",
165 "managed repo is configured for snapshot only");
169 StorageAsset basedir = managedRepository.getRoot();
170 if (VersionUtil.isSnapshot(artifactVersion)) {
171 StorageAsset metadataFile = pathTranslator.toFile(basedir, readMetadataRequest.getNamespace(),
172 readMetadataRequest.getProjectId(), artifactVersion,
175 ArchivaRepositoryMetadata metadata = metadataReader.read(metadataFile);
177 // re-adjust to timestamp if present, otherwise retain the original -SNAPSHOT filename
178 SnapshotVersion snapshotVersion = metadata.getSnapshotVersion();
179 if (snapshotVersion != null) {
181 artifactVersion.substring(0, artifactVersion.length() - 8); // remove SNAPSHOT from end
183 artifactVersion + snapshotVersion.getTimestamp() + "-" + snapshotVersion.getBuildNumber();
185 } catch ( RepositoryMetadataException e) {
186 // unable to parse metadata - LOGGER it, and continue with the version as the original SNAPSHOT version
187 log.warn("Invalid metadata: {} - {}", metadataFile, e.getMessage());
191 // TODO: won't work well with some other layouts, might need to convert artifact parts to ID by path translator
192 String id = readMetadataRequest.getProjectId() + "-" + artifactVersion + ".pom";
194 pathTranslator.toFile(basedir, readMetadataRequest.getNamespace(), readMetadataRequest.getProjectId(),
195 readMetadataRequest.getProjectVersion(), id);
197 if (!file.exists()) {
198 // metadata could not be resolved
199 throw new RepositoryStorageMetadataNotFoundException(
200 "The artifact's POM file '" + file.getPath() + "' was missing");
203 // TODO: this is a workaround until we can properly resolve using proxies as well - this doesn't cache
205 List<RemoteRepository> remoteRepositories = new ArrayList<>();
206 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());
215 if (remoteRepoConfig != null) {
216 remoteRepositories.add(remoteRepoConfig);
218 NetworkProxy networkProxyConfig =
219 proxyRegistry.getNetworkProxy(proxyConnector.getProxyId());
221 if (networkProxyConfig != null) {
222 // key/value: remote repo ID/proxy info
223 networkProxies.put(proxyConnector.getTargetRepository().getId(), networkProxyConfig);
229 // That's a browsing request so we can a mix of SNAPSHOT and release artifacts (especially with snapshots which
230 // can have released parent pom
231 if (readMetadataRequest.isBrowsingRequest()) {
232 remoteRepositories.addAll(repositoryRegistry.getRemoteRepositories());
235 ModelBuildingRequest req =
236 new DefaultModelBuildingRequest().setProcessPlugins(false).setPomFile(file.getFilePath().toFile()).setTwoPhaseBuilding(
237 false).setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MINIMAL);
239 //MRM-1607. olamy this will resolve jdk profiles on the current running archiva jvm
240 req.setSystemProperties(System.getProperties());
243 req.setModelResolver(
244 new RepositoryModelResolver(managedRepository, pathTranslator, wagonFactory, remoteRepositories,
245 networkProxies, managedRepository, mavenSystemManager, metadataReader));
249 model = builder.build(req).getEffectiveModel();
250 } catch (ModelBuildingException e) {
251 String msg = "The artifact's POM file '" + file + "' was invalid: " + e.getMessage();
253 List<ModelProblem> modelProblems = e.getProblems();
254 for (ModelProblem problem : modelProblems) {
255 // MRM-1411, related to MRM-1335
256 // this means that the problem was that the parent wasn't resolved!
257 // olamy really hackhish but fail with java profile so use error message
258 // || ( StringUtils.startsWith( problem.getMessage(), "Failed to determine Java version for profile" ) )
259 // but setTwoPhaseBuilding(true) fix that
260 if (((problem.getException() instanceof FileNotFoundException
261 || problem.getException() instanceof NoSuchFileException
262 ) && e.getModelId() != null &&
263 !e.getModelId().equals(problem.getModelId()))) {
264 log.warn("The artifact's parent POM file '{}' cannot be resolved. "
265 + "Using defaults for project version metadata..", file);
267 ProjectVersionMetadata metadata = new ProjectVersionMetadata();
268 metadata.setId(readMetadataRequest.getProjectVersion());
270 MavenProjectFacet facet = new MavenProjectFacet();
271 facet.setGroupId(readMetadataRequest.getNamespace());
272 facet.setArtifactId(readMetadataRequest.getProjectId());
273 facet.setPackaging("jar");
274 metadata.addFacet(facet);
277 "Error in resolving artifact's parent POM file. " + (problem.getException() == null
278 ? problem.getMessage()
279 : problem.getException().getMessage());
280 RepositoryProblemFacet repoProblemFacet = new RepositoryProblemFacet();
281 repoProblemFacet.setRepositoryId(readMetadataRequest.getRepositoryId());
282 repoProblemFacet.setId(readMetadataRequest.getRepositoryId());
283 repoProblemFacet.setMessage(errMsg);
284 repoProblemFacet.setProblem(errMsg);
285 repoProblemFacet.setProject(readMetadataRequest.getProjectId());
286 repoProblemFacet.setVersion(readMetadataRequest.getProjectVersion());
287 repoProblemFacet.setNamespace(readMetadataRequest.getNamespace());
289 metadata.addFacet(repoProblemFacet);
295 throw new RepositoryStorageMetadataInvalidException("invalid-pom", msg, e);
298 // Check if the POM is in the correct location
299 boolean correctGroupId = readMetadataRequest.getNamespace().equals(model.getGroupId());
300 boolean correctArtifactId = readMetadataRequest.getProjectId().equals(model.getArtifactId());
301 boolean correctVersion = readMetadataRequest.getProjectVersion().equals(model.getVersion());
302 if (!correctGroupId || !correctArtifactId || !correctVersion) {
303 StringBuilder message = new StringBuilder("Incorrect POM coordinates in '" + file + "':");
304 if (!correctGroupId) {
305 message.append("\nIncorrect group ID: ").append(model.getGroupId());
307 if (!correctArtifactId) {
308 message.append("\nIncorrect artifact ID: ").append(model.getArtifactId());
310 if (!correctVersion) {
311 message.append("\nIncorrect version: ").append(model.getVersion());
314 throw new RepositoryStorageMetadataInvalidException("mislocated-pom", message.toString());
317 ProjectVersionMetadata metadata = new ProjectVersionMetadata();
318 metadata.setCiManagement(convertCiManagement(model.getCiManagement()));
319 metadata.setDescription(model.getDescription());
320 metadata.setId(readMetadataRequest.getProjectVersion());
321 metadata.setIssueManagement(convertIssueManagement(model.getIssueManagement()));
322 metadata.setLicenses(convertLicenses(model.getLicenses()));
323 metadata.setMailingLists(convertMailingLists(model.getMailingLists()));
324 metadata.setDependencies(convertDependencies(model.getDependencies()));
325 metadata.setName(model.getName());
326 metadata.setOrganization(convertOrganization(model.getOrganization()));
327 metadata.setScm(convertScm(model.getScm()));
328 metadata.setUrl(model.getUrl());
329 metadata.setProperties(model.getProperties());
331 MavenProjectFacet facet = new MavenProjectFacet();
332 facet.setGroupId(model.getGroupId() != null ? model.getGroupId() : model.getParent().getGroupId());
333 facet.setArtifactId(model.getArtifactId());
334 facet.setPackaging(model.getPackaging());
335 if (model.getParent() != null) {
336 MavenProjectParent parent = new MavenProjectParent();
337 parent.setGroupId(model.getParent().getGroupId());
338 parent.setArtifactId(model.getParent().getArtifactId());
339 parent.setVersion(model.getParent().getVersion());
340 facet.setParent(parent);
342 metadata.addFacet(facet);
349 public void setWagonFactory(WagonFactory wagonFactory) {
350 this.wagonFactory = wagonFactory;
353 private List<org.apache.archiva.metadata.model.Dependency> convertDependencies(List<Dependency> dependencies) {
354 List<org.apache.archiva.metadata.model.Dependency> l = new ArrayList<>();
355 for (Dependency dependency : dependencies) {
356 org.apache.archiva.metadata.model.Dependency newDependency =
357 new org.apache.archiva.metadata.model.Dependency();
358 newDependency.setArtifactId(dependency.getArtifactId());
359 newDependency.setClassifier(dependency.getClassifier());
360 newDependency.setNamespace(dependency.getGroupId());
361 newDependency.setOptional(dependency.isOptional());
362 newDependency.setScope(dependency.getScope());
363 newDependency.setSystemPath(dependency.getSystemPath());
364 newDependency.setType(dependency.getType());
365 newDependency.setVersion(dependency.getVersion());
366 l.add(newDependency);
371 private org.apache.archiva.metadata.model.Scm convertScm(Scm scm) {
372 org.apache.archiva.metadata.model.Scm newScm = null;
374 newScm = new org.apache.archiva.metadata.model.Scm();
375 newScm.setConnection(scm.getConnection());
376 newScm.setDeveloperConnection(scm.getDeveloperConnection());
377 newScm.setUrl(scm.getUrl());
382 private org.apache.archiva.metadata.model.Organization convertOrganization(Organization organization) {
383 org.apache.archiva.metadata.model.Organization org = null;
384 if (organization != null) {
385 org = new org.apache.archiva.metadata.model.Organization();
386 org.setName(organization.getName());
387 org.setUrl(organization.getUrl());
392 private List<org.apache.archiva.metadata.model.License> convertLicenses(List<License> licenses) {
393 List<org.apache.archiva.metadata.model.License> l = new ArrayList<>();
394 for (License license : licenses) {
395 org.apache.archiva.metadata.model.License newLicense = new org.apache.archiva.metadata.model.License();
396 newLicense.setName(license.getName());
397 newLicense.setUrl(license.getUrl());
403 private List<org.apache.archiva.metadata.model.MailingList> convertMailingLists(List<MailingList> mailingLists) {
404 List<org.apache.archiva.metadata.model.MailingList> l = new ArrayList<>();
405 for (MailingList mailingList : mailingLists) {
406 org.apache.archiva.metadata.model.MailingList newMailingList =
407 new org.apache.archiva.metadata.model.MailingList();
408 newMailingList.setName(mailingList.getName());
409 newMailingList.setMainArchiveUrl(mailingList.getArchive());
410 newMailingList.setPostAddress(mailingList.getPost());
411 newMailingList.setSubscribeAddress(mailingList.getSubscribe());
412 newMailingList.setUnsubscribeAddress(mailingList.getUnsubscribe());
413 newMailingList.setOtherArchives(mailingList.getOtherArchives());
414 l.add(newMailingList);
419 private org.apache.archiva.metadata.model.IssueManagement convertIssueManagement(IssueManagement issueManagement) {
420 org.apache.archiva.metadata.model.IssueManagement im = null;
421 if (issueManagement != null) {
422 im = new org.apache.archiva.metadata.model.IssueManagement();
423 im.setSystem(issueManagement.getSystem());
424 im.setUrl(issueManagement.getUrl());
429 private org.apache.archiva.metadata.model.CiManagement convertCiManagement(CiManagement ciManagement) {
430 org.apache.archiva.metadata.model.CiManagement ci = null;
431 if (ciManagement != null) {
432 ci = new org.apache.archiva.metadata.model.CiManagement();
433 ci.setSystem(ciManagement.getSystem());
434 ci.setUrl(ciManagement.getUrl());
440 public Collection<String> listRootNamespaces(String repoId, Filter<String> filter)
441 throws RepositoryStorageRuntimeException {
442 StorageAsset dir = getRepositoryBasedir(repoId);
444 return getSortedFiles(dir, filter);
447 private static Collection<String> getSortedFiles(StorageAsset dir, Filter<String> filter) {
449 final Predicate<StorageAsset> dFilter = new DirectoryFilter(filter);
450 return dir.list().stream().filter(f -> f.isContainer())
452 .map(path -> path.getName().toString())
453 .sorted().collect(Collectors.toList());
457 private StorageAsset getRepositoryBasedir(String repoId)
458 throws RepositoryStorageRuntimeException {
459 ManagedRepository repository = repositoryRegistry.getManagedRepository(repoId);
461 return repository.getRoot();
465 public Collection<String> listNamespaces(String repoId, String namespace, Filter<String> filter)
466 throws RepositoryStorageRuntimeException {
467 StorageAsset dir = pathTranslator.toFile(getRepositoryBasedir(repoId), namespace);
468 if (!(dir.exists()) && !dir.isContainer()) {
469 return Collections.emptyList();
471 // scan all the directories which are potential namespaces. Any directories known to be projects are excluded
472 Predicate<StorageAsset> dFilter = new DirectoryFilter(filter);
473 return dir.list().stream().filter(dFilter).filter(path -> !isProject(path, filter)).map(path -> path.getName().toString())
474 .sorted().collect(Collectors.toList());
478 public Collection<String> listProjects(String repoId, String namespace, Filter<String> filter)
479 throws RepositoryStorageRuntimeException {
480 StorageAsset dir = pathTranslator.toFile(getRepositoryBasedir(repoId), namespace);
481 if (!(dir.exists() && dir.isContainer())) {
482 return Collections.emptyList();
484 // scan all directories in the namespace, and only include those that are known to be projects
485 final Predicate<StorageAsset> dFilter = new DirectoryFilter(filter);
486 return dir.list().stream().filter(dFilter).filter(path -> isProject(path, filter)).map(path -> path.getName().toString())
487 .sorted().collect(Collectors.toList());
492 public Collection<String> listProjectVersions(String repoId, String namespace, String projectId,
493 Filter<String> filter)
494 throws RepositoryStorageRuntimeException {
495 StorageAsset dir = pathTranslator.toFile(getRepositoryBasedir(repoId), namespace, projectId);
496 if (!(dir.exists() && dir.isContainer())) {
497 return Collections.emptyList();
500 // all directories in a project directory can be considered a version
501 return getSortedFiles(dir, filter);
505 public Collection<ArtifactMetadata> readArtifactsMetadata(ReadMetadataRequest readMetadataRequest)
506 throws RepositoryStorageRuntimeException {
507 StorageAsset dir = pathTranslator.toFile(getRepositoryBasedir(readMetadataRequest.getRepositoryId()),
508 readMetadataRequest.getNamespace(), readMetadataRequest.getProjectId(),
509 readMetadataRequest.getProjectVersion());
510 if (!(dir.exists() && dir.isContainer())) {
511 return Collections.emptyList();
514 // all files that are not metadata and not a checksum / signature are considered artifacts
515 final Predicate<StorageAsset> dFilter = new ArtifactDirectoryFilter(readMetadataRequest.getFilter());
516 // Returns a map TRUE -> (success values), FALSE -> (Exceptions)
517 Map<Boolean, List<Try<ArtifactMetadata>>> result = dir.list().stream().filter(dFilter).map(path -> {
519 return Try.success(getArtifactFromFile(readMetadataRequest.getRepositoryId(), readMetadataRequest.getNamespace(),
520 readMetadataRequest.getProjectId(), readMetadataRequest.getProjectVersion(),
522 } catch (Exception e) {
523 log.debug("Could not create metadata for {}: {}", path, e.getMessage(), e);
524 return Try.<ArtifactMetadata>failure(e);
527 ).collect(Collectors.groupingBy(Try::isSuccess));
528 if (result.containsKey(Boolean.FALSE) && result.get(Boolean.FALSE).size() > 0 && (!result.containsKey(Boolean.TRUE) || result.get(Boolean.TRUE).size() == 0)) {
529 log.error("Could not get artifact metadata. Directory: {}. Number of errors {}.", dir, result.get(Boolean.FALSE).size());
530 Try<ArtifactMetadata> failure = result.get(Boolean.FALSE).get(0);
531 log.error("Sample exception {}", failure.getError().getMessage(), failure.getError());
532 throw new RepositoryStorageRuntimeException(readMetadataRequest.getRepositoryId(), "Could not retrieve metadata of the files");
534 if (!result.containsKey(Boolean.TRUE) || result.get(Boolean.TRUE) == null) {
535 return Collections.emptyList();
537 return result.get(Boolean.TRUE).stream().map(tr -> tr.get()).collect(Collectors.toList());
543 public ArtifactMetadata readArtifactMetadataFromPath(String repoId, String path)
544 throws RepositoryStorageRuntimeException {
545 ArtifactMetadata metadata = pathTranslator.getArtifactForPath(repoId, path);
548 populateArtifactMetadataFromFile(metadata, getRepositoryBasedir(repoId).resolve(path));
549 } catch (IOException e) {
550 throw new RepositoryStorageRuntimeException(repoId, "Error during metadata retrieval of " + path + " :" + e.getMessage(), e);
556 private ArtifactMetadata getArtifactFromFile(String repoId, String namespace, String projectId,
557 String projectVersion, StorageAsset file) throws IOException {
558 ArtifactMetadata metadata =
559 pathTranslator.getArtifactFromId(repoId, namespace, projectId, projectVersion, file.getName());
561 populateArtifactMetadataFromFile(metadata, file);
567 public ItemSelector applyServerSideRelocation(ManagedRepository managedRepository, ItemSelector artifactSelector)
568 throws ProxyDownloadException {
569 if ("pom".equals(artifactSelector.getType())) {
570 return artifactSelector;
573 // Build the artifact POM reference
574 BaseRepositoryContentLayout layout;
577 layout = managedRepository.getContent( ).getLayout( BaseRepositoryContentLayout.class );
579 catch ( LayoutException e )
581 throw new ProxyDownloadException( "Could not set layout " + e.getMessage( ), new HashMap<>( ) );
584 RepositoryType repositoryType = managedRepository.getType();
585 if (!proxyRegistry.hasHandler(repositoryType)) {
586 throw new ProxyDownloadException("No proxy handler found for repository type " + repositoryType, new HashMap<>());
591 ItemSelector selector = ArchivaItemSelector.builder( )
592 .withNamespace( artifactSelector.getNamespace( ) )
593 .withProjectId( artifactSelector.getArtifactId( ) )
594 .withArtifactId( artifactSelector.getArtifactId( ) )
595 .withVersion( artifactSelector.getVersion( ) )
596 .withArtifactVersion( artifactSelector.getVersion( ) )
597 .withType( "pom" ).build( );
599 Artifact pom = layout.getArtifact( selector );
601 RepositoryProxyHandler proxyHandler = proxyRegistry.getHandler(repositoryType).get(0);
603 // Get the artifact POM from proxied repositories if needed
604 proxyHandler.fetchFromProxies(managedRepository, pom);
606 // Open and read the POM from the managed repo
609 return artifactSelector;
613 // MavenXpp3Reader leaves the file open, so we need to close it ourselves.
616 try (Reader reader = Channels.newReader(pom.getAsset().getReadChannel(), Charset.defaultCharset().name())) {
617 model = MAVEN_XPP_3_READER.read(reader);
620 DistributionManagement dist = model.getDistributionManagement();
622 Relocation relocation = dist.getRelocation();
623 if (relocation != null) {
624 ArchivaItemSelector.Builder relocatedBuilder = ArchivaItemSelector.builder( );
625 // artifact is relocated : update the repositoryPath
626 if (relocation.getGroupId() != null) {
627 relocatedBuilder.withNamespace( relocation.getGroupId( ) );
629 relocatedBuilder.withNamespace( artifactSelector.getNamespace( ) );
631 if (relocation.getArtifactId() != null) {
632 relocatedBuilder.withArtifactId( relocation.getArtifactId( ) );
634 relocatedBuilder.withArtifactId( artifactSelector.getArtifactId( ) );
636 if (relocation.getVersion() != null)
638 relocatedBuilder.withVersion( relocation.getVersion( ) );
640 relocatedBuilder.withVersion( artifactSelector.getVersion( ) );
642 return relocatedBuilder.withArtifactVersion( artifactSelector.getArtifactVersion( ) )
643 .withClassifier( artifactSelector.getClassifier( ) )
644 .withType( artifactSelector.getType( ) )
645 .withProjectId( artifactSelector.getProjectId( ) )
646 .withExtension( artifactSelector.getExtension( ) )
650 } catch (IOException e) {
651 // Unable to read POM : ignore.
652 } catch (XmlPullParserException e) {
653 // Invalid POM : ignore
655 return artifactSelector;
660 public String getFilePath(String requestPath, org.apache.archiva.repository.ManagedRepository managedRepository) {
661 // managedRepository can be null
662 // extract artifact reference from url
663 // groupId:artifactId:version:packaging:classifier
664 //org/apache/archiva/archiva-checksum/1.4-M4-SNAPSHOT/archiva-checksum-1.4-M4-SNAPSHOT.jar
665 String logicalResource = null;
666 String requestPathInfo = StringUtils.defaultString(requestPath);
668 //remove prefix ie /repository/blah becomes /blah
669 requestPathInfo = removePrefix(requestPathInfo);
671 // Remove prefixing slash as the repository id doesn't contain it;
672 if (requestPathInfo.startsWith("/")) {
673 requestPathInfo = requestPathInfo.substring(1);
676 int slash = requestPathInfo.indexOf('/');
678 logicalResource = requestPathInfo.substring(slash);
680 if (logicalResource.endsWith("/..")) {
681 logicalResource += "/";
684 if (logicalResource != null && logicalResource.startsWith("//")) {
685 logicalResource = logicalResource.substring(1);
688 if (logicalResource == null) {
689 logicalResource = "/";
692 logicalResource = "/";
694 return logicalResource;
699 public String getFilePathWithVersion(final String requestPath, ManagedRepositoryContent managedRepositoryContent)
700 throws RelocationException
703 if (StringUtils.endsWith(requestPath, METADATA_FILENAME)) {
704 return getFilePath(requestPath, managedRepositoryContent.getRepository());
707 String filePath = getFilePath(requestPath, managedRepositoryContent.getRepository());
711 ContentItem item = managedRepositoryContent.toItem( filePath );
712 artifact = managedRepositoryContent.getLayout( BaseRepositoryContentLayout.class ).adaptItem( Artifact.class, item );
714 catch ( LayoutException e )
719 if (VersionUtil.isGenericSnapshot(artifact.getArtifactVersion())) {
720 // read maven metadata to get last timestamp
721 StorageAsset metadataDir = managedRepositoryContent.getRepository().getAsset(filePath).getParent();
722 if (!metadataDir.exists()) {
725 StorageAsset metadataFile = metadataDir.resolve(METADATA_FILENAME);
726 if (!metadataFile.exists()) {
729 ArchivaRepositoryMetadata archivaRepositoryMetadata = null;
732 archivaRepositoryMetadata = metadataReader.read(metadataFile);
734 catch ( RepositoryMetadataException e )
736 log.error( "Could not read metadata {}", e.getMessage( ), e );
739 int buildNumber = archivaRepositoryMetadata.getSnapshotVersion().getBuildNumber();
740 String timestamp = archivaRepositoryMetadata.getSnapshotVersion().getTimestamp();
743 if (buildNumber < 1 && timestamp == null) {
747 // org/apache/archiva/archiva-checksum/1.4-M4-SNAPSHOT/archiva-checksum-1.4-M4-SNAPSHOT.jar
748 // -> archiva-checksum-1.4-M4-20130425.081822-1.jar
750 filePath = StringUtils.replace(filePath, //
752 + "-" + artifact.getVersion().getId(), //
754 + "-" + StringUtils.remove(artifact.getArtifactVersion(),
755 "-" + VersionUtil.SNAPSHOT) //
757 + "-" + buildNumber);
759 throw new RelocationException("/repository/" + managedRepositoryContent.getRepository().getId() +
760 (StringUtils.startsWith(filePath, "/") ? "" : "/") + filePath,
761 RelocationException.RelocationType.TEMPORARY);
768 //-----------------------------
770 //-----------------------------
778 private static String removePrefix(final String href) {
779 String[] parts = StringUtils.split(href, '/');
780 parts = (String[]) ArrayUtils.subarray(parts, 1, parts.length);
781 if (parts == null || parts.length == 0) {
785 String joinedString = StringUtils.join(parts, '/');
786 if (href.endsWith("/")) {
787 joinedString = joinedString + "/";
793 private static void populateArtifactMetadataFromFile(ArtifactMetadata metadata, StorageAsset file) throws IOException {
794 metadata.setWhenGathered(ZonedDateTime.now(ZoneId.of("GMT")));
795 metadata.setFileLastModified(file.getModificationTime().toEpochMilli());
796 ChecksummedFile checksummedFile = new ChecksummedFile(file.getFilePath());
798 metadata.setMd5(checksummedFile.calculateChecksum(ChecksumAlgorithm.MD5));
799 } catch (IOException e) {
800 log.error("Unable to checksum file {}: {},MD5", file, e.getMessage());
803 metadata.setSha1(checksummedFile.calculateChecksum(ChecksumAlgorithm.SHA1));
804 } catch (IOException e) {
805 log.error("Unable to checksum file {}: {},SHA1", file, e.getMessage());
807 metadata.setSize(file.getSize());
810 private boolean isProject(StorageAsset dir, Filter<String> filter) {
811 // scan directories for a valid project version subdirectory, meaning this must be a project directory
812 final Predicate<StorageAsset> dFilter = new DirectoryFilter(filter);
813 boolean projFound = dir.list().stream().filter(dFilter)
814 .anyMatch(path -> isProjectVersion(path));
819 // if a metadata file is present, check if this is the "artifactId" directory, marking it as a project
820 ArchivaRepositoryMetadata metadata = readMetadata(dir);
821 if (metadata != null && dir.getName().toString().equals(metadata.getArtifactId())) {
828 private boolean isProjectVersion(StorageAsset dir) {
829 final String artifactId = dir.getParent().getName();
830 final String projectVersion = dir.getName();
832 // check if there is a POM artifact file to ensure it is a version directory
834 Predicate<StorageAsset> filter;
835 if (VersionUtil.isSnapshot(projectVersion)) {
836 filter = new PomFilenameFilter(artifactId, projectVersion);
838 final String pomFile = artifactId + "-" + projectVersion + ".pom";
839 filter = new PomFileFilter(pomFile);
841 if (dir.list().stream().filter(f -> !f.isContainer()).anyMatch(filter)) {
844 // if a metadata file is present, check if this is the "version" directory, marking it as a project version
845 ArchivaRepositoryMetadata metadata = readMetadata(dir);
846 if (metadata != null && projectVersion.equals(metadata.getVersion())) {
853 private ArchivaRepositoryMetadata readMetadata(StorageAsset directory) {
854 ArchivaRepositoryMetadata metadata = null;
855 StorageAsset metadataFile = directory.resolve(METADATA_FILENAME);
856 if (metadataFile.exists()) {
858 metadata = metadataReader.read(metadataFile);
859 } catch ( RepositoryMetadataException e )
861 // Ignore missing or invalid metadata
867 private static class DirectoryFilter
868 implements Predicate<StorageAsset> {
869 private final Filter<String> filter;
871 public DirectoryFilter(Filter<String> filter) {
872 this.filter = filter;
876 public boolean test(StorageAsset dir) {
877 final String name = dir.getName();
878 if (!filter.accept(name)) {
880 } else if (name.startsWith(".")) {
882 } else if (!dir.isContainer()) {
889 private static class ArtifactDirectoryFilter
890 implements Predicate<StorageAsset> {
891 private final Filter<String> filter;
893 private ArtifactDirectoryFilter(Filter<String> filter) {
894 this.filter = filter;
898 public boolean test(StorageAsset file) {
899 final Set<String> checksumExts = ChecksumAlgorithm.getAllExtensions();
900 final String path = file.getPath();
901 final String name = file.getName();
902 final String extension = StringUtils.substringAfterLast(name, ".").toLowerCase();
903 // TODO compare to logic in maven-repository-layer
904 if (file.isContainer()) {
906 } else if (!filter.accept(name)) {
908 } else if (name.startsWith(".") || path.contains("/.") ) {
910 } else if (checksumExts.contains(extension)) {
912 } else if (Arrays.binarySearch(IGNORED_FILES, name) >= 0) {
915 // some files from remote repositories can have name like maven-metadata-archiva-vm-all-public.xml
916 else if (StringUtils.startsWith(name, METADATA_FILENAME_START) && StringUtils.endsWith(name, ".xml")) {
926 private static final class PomFilenameFilter
927 implements Predicate<StorageAsset> {
929 private final String artifactId, projectVersion;
931 private PomFilenameFilter(String artifactId, String projectVersion) {
932 this.artifactId = artifactId;
933 this.projectVersion = projectVersion;
937 public boolean test(StorageAsset dir) {
938 final String name = dir.getName();
939 if (name.startsWith(artifactId + "-") && name.endsWith(".pom")) {
940 String v = name.substring(artifactId.length() + 1, name.length() - 4);
941 v = VersionUtil.getBaseVersion(v);
942 if (v.equals(projectVersion)) {
951 private static class PomFileFilter
952 implements Predicate<StorageAsset> {
953 private final String pomFile;
955 private PomFileFilter(String pomFile) {
956 this.pomFile = pomFile;
960 public boolean test(StorageAsset dir) {
961 return pomFile.equals(dir.getName());
966 public PathParser getPathParser() {
970 public void setPathParser(PathParser pathParser) {
971 this.pathParser = pathParser;