1 package org.apache.archiva.metadata.repository.file;
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
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import org.apache.archiva.checksum.ChecksumAlgorithm;
23 import org.apache.archiva.configuration.ArchivaConfiguration;
24 import org.apache.archiva.configuration.ManagedRepositoryConfiguration;
25 import org.apache.archiva.metadata.QueryParameter;
26 import org.apache.archiva.metadata.model.ArtifactMetadata;
27 import org.apache.archiva.metadata.model.CiManagement;
28 import org.apache.archiva.metadata.model.Dependency;
29 import org.apache.archiva.metadata.model.IssueManagement;
30 import org.apache.archiva.metadata.model.License;
31 import org.apache.archiva.metadata.model.MailingList;
32 import org.apache.archiva.metadata.model.MetadataFacet;
33 import org.apache.archiva.metadata.model.MetadataFacetFactory;
34 import org.apache.archiva.metadata.model.Organization;
35 import org.apache.archiva.metadata.model.ProjectMetadata;
36 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
37 import org.apache.archiva.metadata.model.ProjectVersionReference;
38 import org.apache.archiva.metadata.model.Scm;
39 import org.apache.archiva.metadata.repository.AbstractMetadataRepository;
40 import org.apache.archiva.metadata.repository.MetadataRepository;
41 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
42 import org.apache.archiva.metadata.repository.MetadataResolutionException;
43 import org.apache.archiva.metadata.repository.MetadataService;
44 import org.apache.archiva.metadata.repository.RepositorySession;
45 import org.apache.commons.lang.StringUtils;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
49 import javax.annotation.ParametersAreNonnullByDefault;
50 import java.io.FileNotFoundException;
51 import java.io.IOException;
52 import java.io.InputStream;
53 import java.io.OutputStream;
54 import java.nio.file.FileVisitOption;
55 import java.nio.file.Files;
56 import java.nio.file.NoSuchFileException;
57 import java.nio.file.Path;
58 import java.nio.file.Paths;
59 import java.time.Instant;
60 import java.time.ZoneId;
61 import java.time.ZonedDateTime;
62 import java.util.ArrayList;
63 import java.util.Arrays;
64 import java.util.Collection;
65 import java.util.Collections;
66 import java.util.Comparator;
67 import java.util.HashMap;
68 import java.util.HashSet;
69 import java.util.LinkedHashSet;
70 import java.util.List;
72 import java.util.Objects;
73 import java.util.Properties;
75 import java.util.StringTokenizer;
76 import java.util.stream.Collectors;
77 import java.util.stream.Stream;
80 * File implementation of the metadata repository. It uses property files in a separate directory tree.
81 * The implementation has no fulltext index. So fulltext queries are not supported.
83 * Some retrieval methods may not be very efficient.
85 @ParametersAreNonnullByDefault
86 public class FileMetadataRepository
87 extends AbstractMetadataRepository implements MetadataRepository {
89 private final ArchivaConfiguration configuration;
91 private Logger log = LoggerFactory.getLogger(FileMetadataRepository.class);
93 private static final String PROJECT_METADATA_KEY = "project-metadata";
95 private static final String PROJECT_VERSION_METADATA_KEY = "version-metadata";
97 private static final String NAMESPACE_METADATA_KEY = "namespace-metadata";
99 private static final String METADATA_KEY = "metadata";
101 private Map<String, Path> baseDirectory = new HashMap<>();
103 public FileMetadataRepository(MetadataService metadataService,
104 ArchivaConfiguration configuration) {
105 super(metadataService);
106 this.configuration = configuration;
109 private Path getBaseDirectory(String repoId)
111 if (!baseDirectory.containsKey(repoId)) {
113 ManagedRepositoryConfiguration managedRepositoryConfiguration =
114 configuration.getConfiguration().getManagedRepositoriesAsMap().get(repoId);
115 if (managedRepositoryConfiguration == null) {
116 baseDir = Files.createTempDirectory(repoId);
118 baseDir = Paths.get(managedRepositoryConfiguration.getLocation());
120 baseDirectory.put(repoId, baseDir.resolve(".archiva"));
122 return baseDirectory.get(repoId);
125 private Path getDirectory(String repoId)
127 return getBaseDirectory(repoId).resolve("content");
131 public void updateProject(RepositorySession session, String repoId, ProjectMetadata project) {
132 updateProject(session, repoId, project.getNamespace(), project.getId());
135 private void updateProject(RepositorySession session, String repoId, String namespace, String id) {
136 // TODO: this is a more braindead implementation than we would normally expect, for prototyping purposes
137 updateNamespace(session, repoId, namespace);
140 Path namespaceDirectory = getDirectory(repoId).resolve(namespace);
141 Properties properties = new Properties();
142 properties.setProperty("namespace", namespace);
143 properties.setProperty("id", id);
144 writeProperties(properties, namespaceDirectory.resolve(id), PROJECT_METADATA_KEY);
145 } catch (IOException e) {
146 log.error("Could not update project {}, {}, {}: {}", repoId, namespace, id, e.getMessage(), e);
151 public void updateProjectVersion(RepositorySession session, String repoId, String namespace, String projectId,
152 ProjectVersionMetadata versionMetadata) {
155 updateProject(session, repoId, namespace, projectId);
158 getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + versionMetadata.getId());
160 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
161 // remove properties that are not references or artifacts
162 for (Object key : new ArrayList<>(properties.keySet())) {
163 String name = (String) key;
164 if (!name.contains(":") && !name.equals("facetIds")) {
165 properties.remove(name);
168 // clear the facet contents so old properties are no longer written
169 clearMetadataFacetProperties(versionMetadata.getFacetList(), properties, "");
171 properties.setProperty("id", versionMetadata.getId());
172 setProperty(properties, "name", versionMetadata.getName());
173 setProperty(properties, "description", versionMetadata.getDescription());
174 setProperty(properties, "url", versionMetadata.getUrl());
175 setProperty(properties, "incomplete", String.valueOf(versionMetadata.isIncomplete()));
176 if (versionMetadata.getScm() != null) {
177 setProperty(properties, "scm.connection", versionMetadata.getScm().getConnection());
178 setProperty(properties, "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection());
179 setProperty(properties, "scm.url", versionMetadata.getScm().getUrl());
181 if (versionMetadata.getCiManagement() != null) {
182 setProperty(properties, "ci.system", versionMetadata.getCiManagement().getSystem());
183 setProperty(properties, "ci.url", versionMetadata.getCiManagement().getUrl());
185 if (versionMetadata.getIssueManagement() != null) {
186 setProperty(properties, "issue.system", versionMetadata.getIssueManagement().getSystem());
187 setProperty(properties, "issue.url", versionMetadata.getIssueManagement().getUrl());
189 if (versionMetadata.getOrganization() != null) {
190 setProperty(properties, "org.name", versionMetadata.getOrganization().getName());
191 setProperty(properties, "org.url", versionMetadata.getOrganization().getUrl());
194 for (License license : versionMetadata.getLicenses()) {
195 setProperty(properties, "license." + i + ".name", license.getName());
196 setProperty(properties, "license." + i + ".url", license.getUrl());
200 for (MailingList mailingList : versionMetadata.getMailingLists()) {
201 setProperty(properties, "mailingList." + i + ".archive", mailingList.getMainArchiveUrl());
202 setProperty(properties, "mailingList." + i + ".name", mailingList.getName());
203 setProperty(properties, "mailingList." + i + ".post", mailingList.getPostAddress());
204 setProperty(properties, "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress());
205 setProperty(properties, "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress());
206 setProperty(properties, "mailingList." + i + ".otherArchives",
207 join(mailingList.getOtherArchives()));
211 ProjectVersionReference reference = new ProjectVersionReference();
212 reference.setNamespace(namespace);
213 reference.setProjectId(projectId);
214 reference.setProjectVersion(versionMetadata.getId());
215 reference.setReferenceType(ProjectVersionReference.ReferenceType.DEPENDENCY);
216 for (Dependency dependency : versionMetadata.getDependencies()) {
217 setProperty(properties, "dependency." + i + ".classifier", dependency.getClassifier());
218 setProperty(properties, "dependency." + i + ".scope", dependency.getScope());
219 setProperty(properties, "dependency." + i + ".systemPath", dependency.getSystemPath());
220 setProperty(properties, "dependency." + i + ".artifactId", dependency.getArtifactId());
221 setProperty(properties, "dependency." + i + ".groupId", dependency.getGroupId());
222 setProperty(properties, "dependency." + i + ".version", dependency.getVersion());
223 setProperty(properties, "dependency." + i + ".type", dependency.getType());
224 setProperty(properties, "dependency." + i + ".optional", String.valueOf(dependency.isOptional()));
226 updateProjectReference(repoId, dependency.getGroupId(), dependency.getArtifactId(),
227 dependency.getVersion(), reference);
231 Set<String> facetIds = new LinkedHashSet<>(versionMetadata.getFacetIds());
232 facetIds.addAll(Arrays.asList(properties.getProperty("facetIds", "").split(",")));
233 properties.setProperty("facetIds", join(facetIds));
235 updateProjectVersionFacets(versionMetadata, properties);
237 writeProperties(properties, directory, PROJECT_VERSION_METADATA_KEY);
238 } catch (IOException e) {
239 log.error("Could not update project version {}, {}, {}: {}", repoId, namespace, versionMetadata.getId(), e.getMessage(), e);
243 private void updateProjectVersionFacets(ProjectVersionMetadata versionMetadata, Properties properties) {
244 for (MetadataFacet facet : versionMetadata.getFacetList()) {
245 for (Map.Entry<String, String> entry : facet.toProperties().entrySet()) {
246 properties.setProperty(facet.getFacetId() + ":" + entry.getKey(), entry.getValue());
251 private static void clearMetadataFacetProperties(Collection<MetadataFacet> facetList, Properties properties,
253 List<Object> propsToRemove = new ArrayList<>();
254 for (MetadataFacet facet : facetList) {
255 for (Object key : new ArrayList<>(properties.keySet())) {
256 String keyString = (String) key;
257 if (keyString.startsWith(prefix + facet.getFacetId() + ":")) {
258 propsToRemove.add(key);
263 for (Object key : propsToRemove) {
264 properties.remove(key);
268 private void updateProjectReference(String repoId, String namespace, String projectId, String projectVersion,
269 ProjectVersionReference reference) {
271 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
273 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
274 int i = Integer.parseInt(properties.getProperty("ref:lastReferenceNum", "-1")) + 1;
275 setProperty(properties, "ref:lastReferenceNum", Integer.toString(i));
276 setProperty(properties, "ref:reference." + i + ".namespace", reference.getNamespace());
277 setProperty(properties, "ref:reference." + i + ".projectId", reference.getProjectId());
278 setProperty(properties, "ref:reference." + i + ".projectVersion", reference.getProjectVersion());
279 setProperty(properties, "ref:reference." + i + ".referenceType", reference.getReferenceType().toString());
281 writeProperties(properties, directory, PROJECT_VERSION_METADATA_KEY);
282 } catch (IOException e) {
283 log.error("Could not update project reference {}, {}, {}, {}: {}", repoId, namespace, projectId, projectVersion, e.getMessage(), e);
288 public void updateNamespace(RepositorySession session, String repoId, String namespace) {
290 Path namespaceDirectory = getDirectory(repoId).resolve(namespace);
291 Properties properties = new Properties();
292 properties.setProperty("namespace", namespace);
293 writeProperties(properties, namespaceDirectory, NAMESPACE_METADATA_KEY);
295 } catch (IOException e) {
296 log.error("Could not update namespace of {}, {}: {}", repoId, namespace, e.getMessage(), e);
301 public List<String> getMetadataFacets(RepositorySession session, String repoId, String facetId)
302 throws MetadataRepositoryException {
304 Path directory = getMetadataDirectory(repoId, facetId);
305 if (!(Files.exists(directory) && Files.isDirectory(directory))) {
306 return Collections.emptyList();
309 final String searchFile = METADATA_KEY + ".properties";
310 try (Stream<Path> fs = Files.walk(directory, FileVisitOption.FOLLOW_LINKS)) {
311 facets = fs.filter(Files::isDirectory).filter(path -> Files.exists(path.resolve(searchFile)))
312 .map(path -> directory.relativize(path).toString()).collect(Collectors.toList());
315 } catch (IOException e) {
316 throw new MetadataRepositoryException(e.getMessage(), e);
321 public <T extends MetadataFacet> Stream<T> getMetadataFacetStream(RepositorySession session, String repositoryId, Class<T> facetClazz, QueryParameter queryParameter) throws MetadataRepositoryException {
322 final MetadataFacetFactory<T> metadataFacetFactory = getFacetFactory(facetClazz);
323 if (metadataFacetFactory == null) {
326 final String facetId = metadataFacetFactory.getFacetId();
327 final String searchFile = METADATA_KEY + ".properties";
329 Path directory = getMetadataDirectory(repositoryId, facetId);
330 return Files.walk(directory, FileVisitOption.FOLLOW_LINKS).filter(Files::isDirectory)
331 .filter(path -> Files.exists(path.resolve(searchFile)))
332 .map(path -> directory.relativize(path).toString())
334 .skip(queryParameter.getOffset())
335 .limit(queryParameter.getLimit())
336 .map(name -> getMetadataFacet(session, repositoryId, facetClazz, name));
337 } catch (IOException e) {
338 throw new MetadataRepositoryException(e.getMessage(), e);
343 public boolean hasMetadataFacet(RepositorySession session, String repositoryId, String facetId)
344 throws MetadataRepositoryException {
347 Path directory = getMetadataDirectory(repositoryId, facetId);
348 if (!(Files.exists(directory) && Files.isDirectory(directory))) {
351 final String searchFile = METADATA_KEY + ".properties";
352 try (Stream<Path> fs = Files.walk(directory, FileVisitOption.FOLLOW_LINKS)) {
353 return fs.filter(Files::isDirectory).anyMatch(path -> Files.exists(path.resolve(searchFile)));
355 } catch (IOException e) {
356 log.error("Could not retrieve facet metatadata {}, {}: {}", repositoryId, facetId, e.getMessage(), e);
357 throw new MetadataRepositoryException(e.getMessage(), e);
364 public <T extends MetadataFacet> T getMetadataFacet(RepositorySession session, String repositoryId, Class<T> facetClazz, String name) {
365 final MetadataFacetFactory<T> metadataFacetFactory = getFacetFactory(facetClazz);
366 if (metadataFacetFactory == null) {
369 final String facetId = metadataFacetFactory.getFacetId();
371 Properties properties;
374 readProperties(getMetadataDirectory(repositoryId, facetId).resolve(name), METADATA_KEY);
375 } catch (NoSuchFileException | FileNotFoundException e) {
377 } catch (IOException e) {
378 log.error("Could not read properties from {}, {}: {}", repositoryId, facetId, e.getMessage(), e);
381 T metadataFacet = null;
382 if (metadataFacetFactory != null) {
383 metadataFacet = metadataFacetFactory.createMetadataFacet(repositoryId, name);
384 Map<String, String> map = new HashMap<>();
385 for (Object key : new ArrayList<>(properties.keySet())) {
386 String property = (String) key;
387 map.put(property, properties.getProperty(property));
389 metadataFacet.fromProperties(map);
391 return metadataFacet;
396 public void addMetadataFacet(RepositorySession session, String repositoryId, MetadataFacet metadataFacet) {
397 Properties properties = new Properties();
398 properties.putAll(metadataFacet.toProperties());
402 getMetadataDirectory(repositoryId, metadataFacet.getFacetId()).resolve(metadataFacet.getName());
403 writeProperties(properties, directory, METADATA_KEY);
404 } catch (IOException e) {
406 log.error(e.getMessage(), e);
411 public void removeMetadataFacets(RepositorySession session, String repositoryId, String facetId)
412 throws MetadataRepositoryException {
414 Path dir = getMetadataDirectory(repositoryId, facetId);
415 org.apache.archiva.common.utils.FileUtils.deleteDirectory(dir);
416 } catch (IOException e) {
417 throw new MetadataRepositoryException(e.getMessage(), e);
422 public void removeMetadataFacet(RepositorySession session, String repoId, String facetId, String name)
423 throws MetadataRepositoryException {
425 Path dir = getMetadataDirectory(repoId, facetId).resolve(name);
426 org.apache.archiva.common.utils.FileUtils.deleteDirectory(dir);
427 } catch (IOException e) {
428 throw new MetadataRepositoryException(e.getMessage(), e);
433 public List<ArtifactMetadata> getArtifactsByDateRange(RepositorySession session, String repoId, ZonedDateTime startTime, ZonedDateTime endTime)
434 throws MetadataRepositoryException {
436 List<ArtifactMetadata> artifacts = new ArrayList<>();
437 for (String ns : getRootNamespaces(session, repoId)) {
438 getArtifactsByDateRange(session, artifacts, repoId, ns, startTime, endTime);
440 artifacts.sort(new ArtifactComparator());
442 } catch (MetadataResolutionException e) {
443 throw new MetadataRepositoryException(e.getMessage(), e);
449 * Result is sorted by date,
451 * @param session The repository session
452 * @param repositoryId The repository id
453 * @param startTime The start time, can be <code>null</code>
454 * @param endTime The end time, can be <code>null</code>
455 * @param queryParameter Additional parameters for the query that affect ordering and number of returned results.
457 * @throws MetadataRepositoryException
460 public Stream<ArtifactMetadata> getArtifactByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException {
462 List<ArtifactMetadata> artifacts = new ArrayList<>();
463 for (String ns : getRootNamespaces(session, repositoryId)) {
464 getArtifactsByDateRange(session, artifacts, repositoryId, ns, startTime, endTime);
466 Comparator<ArtifactMetadata> comp = getArtifactMetadataComparator(queryParameter, "whenGathered");
467 return artifacts.stream().sorted(comp).skip(queryParameter.getOffset()).limit(queryParameter.getLimit());
469 } catch (MetadataResolutionException e) {
470 throw new MetadataRepositoryException(e.getMessage(), e);
475 private void getArtifactsByDateRange(RepositorySession session, List<ArtifactMetadata> artifacts, String repoId, String ns, ZonedDateTime startTime,
476 ZonedDateTime endTime)
477 throws MetadataRepositoryException {
479 for (String namespace : this.getChildNamespaces(session, repoId, ns)) {
480 getArtifactsByDateRange(session, artifacts, repoId, ns + "." + namespace, startTime, endTime);
483 for (String project : getProjects(session, repoId, ns)) {
484 for (String version : getProjectVersions(session, repoId, ns, project)) {
485 for (ArtifactMetadata artifact : getArtifacts(session, repoId, ns, project, version)) {
486 if (startTime == null || startTime.isBefore(ZonedDateTime.from(artifact.getWhenGathered().toInstant().atZone(ZoneId.systemDefault())))) {
487 if (endTime == null || endTime.isAfter(ZonedDateTime.from(artifact.getWhenGathered().toInstant().atZone(ZoneId.systemDefault())))) {
488 artifacts.add(artifact);
494 } catch (MetadataResolutionException e) {
495 throw new MetadataRepositoryException(e.getMessage(), e);
501 public Collection<ArtifactMetadata> getArtifacts(RepositorySession session, String repoId, String namespace, String projectId,
502 String projectVersion)
503 throws MetadataResolutionException {
505 Map<String, ArtifactMetadata> artifacts = new HashMap<>();
507 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
509 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
511 for (Map.Entry entry : properties.entrySet()) {
512 String name = (String) entry.getKey();
513 StringTokenizer tok = new StringTokenizer(name, ":");
514 if (tok.hasMoreTokens() && "artifact".equals(tok.nextToken())) {
515 String field = tok.nextToken();
516 String id = tok.nextToken();
518 ArtifactMetadata artifact = artifacts.get(id);
519 if (artifact == null) {
520 artifact = new ArtifactMetadata();
521 artifact.setRepositoryId(repoId);
522 artifact.setNamespace(namespace);
523 artifact.setProject(projectId);
524 artifact.setProjectVersion(projectVersion);
525 artifact.setVersion(projectVersion);
527 artifacts.put(id, artifact);
530 String value = (String) entry.getValue();
531 if ("updated".equals(field)) {
532 artifact.setFileLastModified(Long.parseLong(value));
533 } else if ("size".equals(field)) {
534 artifact.setSize(Long.valueOf(value));
535 } else if ("whenGathered".equals(field)) {
536 artifact.setWhenGathered(ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(value)), ZoneId.of("GMT")));
537 } else if ("version".equals(field)) {
538 artifact.setVersion(value);
539 } else if (field.startsWith("checksum")) {
540 String algorithmStr = StringUtils.removeStart( name, "artifact:checksum:"+id+":");
541 artifact.setChecksum( ChecksumAlgorithm.valueOf( algorithmStr ), value );
542 } else if ("facetIds".equals(field)) {
543 if (value.length() > 0) {
544 String propertyPrefix = "artifact:facet:" + id + ":";
545 for (String facetId : value.split(",")) {
546 MetadataFacetFactory factory = getFacetFactory(facetId);
547 if (factory == null) {
548 log.error("Attempted to load unknown artifact metadata facet: {}", facetId);
550 MetadataFacet facet = factory.createMetadataFacet();
551 String prefix = propertyPrefix + facet.getFacetId();
552 Map<String, String> map = new HashMap<>();
553 for (Object key : new ArrayList<>(properties.keySet())) {
554 String property = (String) key;
555 if (property.startsWith(prefix)) {
556 map.put(property.substring(prefix.length() + 1),
557 properties.getProperty(property));
560 facet.fromProperties(map);
561 artifact.addFacet(facet);
566 updateArtifactFacets(artifact, properties);
570 return artifacts.values();
571 } catch (IOException e) {
572 throw new MetadataResolutionException(e.getMessage(), e);
578 public void close() {
579 // nothing additional to close
583 private void updateArtifactFacets(ArtifactMetadata artifact, Properties properties) {
584 String propertyPrefix = "artifact:facet:" + artifact.getId() + ":";
585 for (MetadataFacet facet : artifact.getFacetList()) {
586 for (Map.Entry<String, String> e : facet.toProperties().entrySet()) {
587 String key = propertyPrefix + facet.getFacetId() + ":" + e.getKey();
588 properties.setProperty(key, e.getValue());
594 public List<ArtifactMetadata> getArtifactsByChecksum(RepositorySession session, String repositoryId, String checksum)
595 throws MetadataRepositoryException {
597 // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
598 // of this information (eg. in Lucene, as before)
599 // alternatively, we could build a referential tree in the content repository, however it would need some levels
600 // of depth to avoid being too broad to be useful (eg. /repository/checksums/a/ab/abcdef1234567)
602 return getArtifactStream( session, repositoryId ).filter(
603 a -> a.hasChecksum( checksum )
604 ).collect( Collectors.toList() );
605 } catch (MetadataResolutionException e) {
606 throw new MetadataRepositoryException(e.getMessage(), e);
611 public void removeNamespace(RepositorySession session, String repositoryId, String project)
612 throws MetadataRepositoryException {
614 Path namespaceDirectory = getDirectory(repositoryId).resolve(project);
615 org.apache.archiva.common.utils.FileUtils.deleteDirectory(namespaceDirectory);
616 //Properties properties = new Properties();
617 //properties.setProperty( "namespace", namespace );
618 //writeProperties( properties, namespaceDirectory, NAMESPACE_METADATA_KEY );
620 } catch (IOException e) {
621 throw new MetadataRepositoryException(e.getMessage(), e);
626 public void removeTimestampedArtifact( RepositorySession session, ArtifactMetadata artifactMetadata, String baseVersion)
627 throws MetadataRepositoryException {
630 Path directory = getDirectory(artifactMetadata.getRepositoryId()).resolve(
631 artifactMetadata.getNamespace() + "/" + artifactMetadata.getProject() + "/"
634 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
636 String id = artifactMetadata.getId();
638 properties.remove("artifact:updated:" + id);
639 properties.remove("artifact:whenGathered:" + id);
640 properties.remove("artifact:size:" + id);
641 artifactMetadata.getChecksums().entrySet().stream().forEach( entry ->
642 properties.remove( "artifact:checksum:"+id+":"+entry.getKey().name() ));
643 properties.remove("artifact:version:" + id);
644 properties.remove("artifact:facetIds:" + id);
646 String prefix = "artifact:facet:" + id + ":";
647 for (Object key : new ArrayList<>(properties.keySet())) {
648 String property = (String) key;
649 if (property.startsWith(prefix)) {
650 properties.remove(property);
654 writeProperties(properties, directory, PROJECT_VERSION_METADATA_KEY);
655 } catch (IOException e) {
656 throw new MetadataRepositoryException(e.getMessage(), e);
662 public void removeArtifact(RepositorySession session, String repoId, String namespace, String project, String version, String id)
663 throws MetadataRepositoryException {
665 Path directory = getDirectory(repoId).resolve(namespace + "/" + project + "/" + version);
667 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
669 properties.remove("artifact:updated:" + id);
670 properties.remove("artifact:whenGathered:" + id);
671 properties.remove("artifact:size:" + id);
672 properties.remove("artifact:version:" + id);
673 properties.remove("artifact:facetIds:" + id);
675 String facetPrefix = "artifact:facet:" + id + ":";
676 String checksumPrefix = "artifact:checksum:"+id+":";
677 for (String property : properties.stringPropertyNames()) {
678 if (property.startsWith( checksumPrefix )) {
679 properties.remove( property );
680 } else if (property.startsWith(facetPrefix)) {
681 properties.remove(property);
685 org.apache.archiva.common.utils.FileUtils.deleteDirectory(directory);
686 //writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
687 } catch (IOException e) {
688 throw new MetadataRepositoryException(e.getMessage(), e);
693 * FIXME implements this !!!!
696 * @param repositoryId
699 * @param projectVersion
700 * @param metadataFacet will remove artifacts which have this {@link MetadataFacet} using equals
701 * @throws MetadataRepositoryException
704 public void removeFacetFromArtifact( RepositorySession session, String repositoryId, String namespace, String project, String projectVersion,
705 MetadataFacet metadataFacet)
706 throws MetadataRepositoryException {
707 throw new UnsupportedOperationException("not implemented");
711 public void removeRepository(RepositorySession session, String repoId)
712 throws MetadataRepositoryException {
714 Path dir = getDirectory(repoId);
715 org.apache.archiva.common.utils.FileUtils.deleteDirectory(dir);
716 } catch (IOException e) {
717 throw new MetadataRepositoryException(e.getMessage(), e);
723 public List<ArtifactMetadata> getArtifactsByProjectVersionFacet( RepositorySession session, String key, String value, String repositoryId)
724 throws MetadataRepositoryException {
725 throw new UnsupportedOperationException("not yet implemented in File backend");
729 public List<ArtifactMetadata> getArtifactsByAttribute( RepositorySession session, String key, String value, String repositoryId)
730 throws MetadataRepositoryException {
731 throw new UnsupportedOperationException("not yet implemented in File backend");
735 public List<ArtifactMetadata> getArtifactsByProjectVersionAttribute( RepositorySession session, String key, String value, String repositoryId)
736 throws MetadataRepositoryException {
737 throw new UnsupportedOperationException("getArtifactsByProjectVersionAttribute not yet implemented in File backend");
740 private Path getMetadataDirectory(String repoId, String facetId)
742 return getBaseDirectory(repoId).resolve("facets/" + facetId);
745 private String join(Collection<String> ids) {
746 if (ids != null && !ids.isEmpty()) {
747 StringBuilder s = new StringBuilder();
748 for (String id : ids) {
752 return s.substring(0, s.length() - 1);
757 private void setProperty(Properties properties, String name, String value) {
759 properties.setProperty(name, value);
764 public void updateArtifact(RepositorySession session, String repoId, String namespace, String projectId, String projectVersion,
765 ArtifactMetadata artifact) {
767 ProjectVersionMetadata metadata = new ProjectVersionMetadata();
768 metadata.setId(projectVersion);
769 updateProjectVersion(session, repoId, namespace, projectId, metadata);
771 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
773 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
775 clearMetadataFacetProperties(artifact.getFacetList(), properties,
776 "artifact:facet:" + artifact.getId() + ":");
778 String id = artifact.getId();
779 properties.setProperty("artifact:updated:" + id,
780 Long.toString(artifact.getFileLastModified().toInstant().toEpochMilli()));
781 properties.setProperty("artifact:whenGathered:" + id,
782 Long.toString(artifact.getWhenGathered().toInstant().toEpochMilli()));
783 properties.setProperty("artifact:size:" + id, Long.toString(artifact.getSize()));
784 artifact.getChecksums().entrySet().stream().forEach( entry ->
785 properties.setProperty( "artifact:checksum:"+id+":"+entry.getKey().name(), entry.getValue() ));
786 properties.setProperty("artifact:version:" + id, artifact.getVersion());
788 Set<String> facetIds = new LinkedHashSet<>(artifact.getFacetIds());
789 String property = "artifact:facetIds:" + id;
790 facetIds.addAll(Arrays.asList(properties.getProperty(property, "").split(",")));
791 properties.setProperty(property, join(facetIds));
793 updateArtifactFacets(artifact, properties);
795 writeProperties(properties, directory, PROJECT_VERSION_METADATA_KEY);
796 } catch (IOException e) {
798 log.error(e.getMessage(), e);
802 private Properties readOrCreateProperties(Path directory, String propertiesKey) {
804 return readProperties(directory, propertiesKey);
805 } catch (FileNotFoundException | NoSuchFileException e) {
806 // ignore and return new properties
807 } catch (IOException e) {
809 log.error(e.getMessage(), e);
811 return new Properties();
814 private Properties readProperties(Path directory, String propertiesKey)
816 Properties properties = new Properties();
817 try (InputStream in = Files.newInputStream(directory.resolve(propertiesKey + ".properties"))) {
825 public ProjectMetadata getProject(RepositorySession session, String repoId, String namespace, String projectId)
826 throws MetadataResolutionException {
828 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId);
830 Properties properties = readOrCreateProperties(directory, PROJECT_METADATA_KEY);
832 ProjectMetadata project = null;
834 String id = properties.getProperty("id");
836 project = new ProjectMetadata();
837 project.setNamespace(properties.getProperty("namespace"));
842 } catch (IOException e) {
843 throw new MetadataResolutionException(e.getMessage(), e);
848 public ProjectVersionMetadata getProjectVersion(RepositorySession session, String repoId, String namespace, String projectId,
849 String projectVersion)
850 throws MetadataResolutionException {
852 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
854 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
855 String id = properties.getProperty("id");
856 ProjectVersionMetadata versionMetadata = null;
858 versionMetadata = new ProjectVersionMetadata();
859 versionMetadata.setId(id);
860 versionMetadata.setName(properties.getProperty("name"));
861 versionMetadata.setDescription(properties.getProperty("description"));
862 versionMetadata.setUrl(properties.getProperty("url"));
863 versionMetadata.setIncomplete(Boolean.valueOf(properties.getProperty("incomplete", "false")));
865 String scmConnection = properties.getProperty("scm.connection");
866 String scmDeveloperConnection = properties.getProperty("scm.developerConnection");
867 String scmUrl = properties.getProperty("scm.url");
868 if (scmConnection != null || scmDeveloperConnection != null || scmUrl != null) {
870 scm.setConnection(scmConnection);
871 scm.setDeveloperConnection(scmDeveloperConnection);
873 versionMetadata.setScm(scm);
876 String ciSystem = properties.getProperty("ci.system");
877 String ciUrl = properties.getProperty("ci.url");
878 if (ciSystem != null || ciUrl != null) {
879 CiManagement ci = new CiManagement();
880 ci.setSystem(ciSystem);
882 versionMetadata.setCiManagement(ci);
885 String issueSystem = properties.getProperty("issue.system");
886 String issueUrl = properties.getProperty("issue.url");
887 if (issueSystem != null || issueUrl != null) {
888 IssueManagement issueManagement = new IssueManagement();
889 issueManagement.setSystem(issueSystem);
890 issueManagement.setUrl(issueUrl);
891 versionMetadata.setIssueManagement(issueManagement);
894 String orgName = properties.getProperty("org.name");
895 String orgUrl = properties.getProperty("org.url");
896 if (orgName != null || orgUrl != null) {
897 Organization org = new Organization();
898 org.setName(orgName);
900 versionMetadata.setOrganization(org);
903 boolean done = false;
906 String licenseName = properties.getProperty("license." + i + ".name");
907 String licenseUrl = properties.getProperty("license." + i + ".url");
908 if (licenseName != null || licenseUrl != null) {
909 License license = new License();
910 license.setName(licenseName);
911 license.setUrl(licenseUrl);
912 versionMetadata.addLicense(license);
922 String mailingListName = properties.getProperty("mailingList." + i + ".name");
923 if (mailingListName != null) {
924 MailingList mailingList = new MailingList();
925 mailingList.setName(mailingListName);
926 mailingList.setMainArchiveUrl(properties.getProperty("mailingList." + i + ".archive"));
927 String p = properties.getProperty("mailingList." + i + ".otherArchives");
928 if (p != null && p.length() > 0) {
929 mailingList.setOtherArchives(Arrays.asList(p.split(",")));
931 mailingList.setOtherArchives(Collections.emptyList());
933 mailingList.setPostAddress(properties.getProperty("mailingList." + i + ".post"));
934 mailingList.setSubscribeAddress(properties.getProperty("mailingList." + i + ".subscribe"));
935 mailingList.setUnsubscribeAddress(
936 properties.getProperty("mailingList." + i + ".unsubscribe"));
937 versionMetadata.addMailingList(mailingList);
947 String dependencyArtifactId = properties.getProperty("dependency." + i + ".artifactId");
948 if (dependencyArtifactId != null) {
949 Dependency dependency = new Dependency();
950 dependency.setArtifactId(dependencyArtifactId);
951 dependency.setGroupId(properties.getProperty("dependency." + i + ".groupId"));
952 dependency.setClassifier(properties.getProperty("dependency." + i + ".classifier"));
953 dependency.setOptional(
954 Boolean.valueOf(properties.getProperty("dependency." + i + ".optional")));
955 dependency.setScope(properties.getProperty("dependency." + i + ".scope"));
956 dependency.setSystemPath(properties.getProperty("dependency." + i + ".systemPath"));
957 dependency.setType(properties.getProperty("dependency." + i + ".type"));
958 dependency.setVersion(properties.getProperty("dependency." + i + ".version"));
959 dependency.setOptional(
960 Boolean.valueOf(properties.getProperty("dependency." + i + ".optional")));
961 versionMetadata.addDependency(dependency);
968 String facetIds = properties.getProperty("facetIds", "");
969 if (facetIds.length() > 0) {
970 for (String facetId : facetIds.split(",")) {
971 MetadataFacetFactory factory = getFacetFactory(facetId);
972 if (factory == null) {
973 log.error("Attempted to load unknown project version metadata facet: {}", facetId);
975 MetadataFacet facet = factory.createMetadataFacet();
976 Map<String, String> map = new HashMap<>();
977 for (Object key : new ArrayList<>(properties.keySet())) {
978 String property = (String) key;
979 if (property.startsWith(facet.getFacetId())) {
980 map.put(property.substring(facet.getFacetId().length() + 1),
981 properties.getProperty(property));
984 facet.fromProperties(map);
985 versionMetadata.addFacet(facet);
990 updateProjectVersionFacets(versionMetadata, properties);
992 return versionMetadata;
993 } catch (IOException e) {
994 throw new MetadataResolutionException(e.getMessage(), e);
999 public Collection<String> getArtifactVersions(RepositorySession session, String repoId, String namespace, String projectId,
1000 String projectVersion)
1001 throws MetadataResolutionException {
1003 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
1005 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
1007 Set<String> versions = new HashSet<>();
1008 for (Map.Entry entry : properties.entrySet()) {
1009 String name = (String) entry.getKey();
1010 if (name.startsWith("artifact:version:")) {
1011 versions.add((String) entry.getValue());
1015 } catch (IOException e) {
1016 throw new MetadataResolutionException(e.getMessage(), e);
1021 public Collection<ProjectVersionReference> getProjectReferences(RepositorySession session, String repoId, String namespace, String projectId,
1022 String projectVersion)
1023 throws MetadataResolutionException {
1025 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
1027 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
1028 int numberOfRefs = Integer.parseInt(properties.getProperty("ref:lastReferenceNum", "-1")) + 1;
1030 List<ProjectVersionReference> references = new ArrayList<>();
1031 for (int i = 0; i < numberOfRefs; i++) {
1032 ProjectVersionReference reference = new ProjectVersionReference();
1033 reference.setProjectId(properties.getProperty("ref:reference." + i + ".projectId"));
1034 reference.setNamespace(properties.getProperty("ref:reference." + i + ".namespace"));
1035 reference.setProjectVersion(properties.getProperty("ref:reference." + i + ".projectVersion"));
1036 reference.setReferenceType(ProjectVersionReference.ReferenceType.valueOf(
1037 properties.getProperty("ref:reference." + i + ".referenceType")));
1038 references.add(reference);
1041 } catch (IOException e) {
1042 throw new MetadataResolutionException(e.getMessage(), e);
1047 public Collection<String> getRootNamespaces(RepositorySession session, String repoId)
1048 throws MetadataResolutionException {
1049 return this.getChildNamespaces(session, repoId, null);
1052 private Stream<String> getAllNamespacesStream(RepositorySession session, String repoId) {
1053 Path directory = null;
1056 directory = getDirectory(repoId);
1058 catch ( IOException e )
1060 return Stream.empty( );
1062 if (!(Files.exists(directory) && Files.isDirectory(directory))) {
1063 return Stream.empty( );
1065 final String searchFile = NAMESPACE_METADATA_KEY + ".properties";
1068 return Files.list(directory).filter(Files::isDirectory).filter(path ->
1069 Files.exists(path.resolve(searchFile))
1070 ).map(path -> path.getFileName().toString());
1072 catch ( IOException e )
1074 return Stream.empty( );
1079 public Collection<String> getChildNamespaces( RepositorySession session, String repoId, String baseNamespace)
1080 throws MetadataResolutionException {
1082 List<String> allNamespaces;
1083 Path directory = getDirectory(repoId);
1084 if (!(Files.exists(directory) && Files.isDirectory(directory))) {
1085 return Collections.emptyList();
1087 final String searchFile = NAMESPACE_METADATA_KEY + ".properties";
1088 try (Stream<Path> fs = Files.list(directory)) {
1089 allNamespaces = fs.filter(Files::isDirectory).filter(path ->
1090 Files.exists(path.resolve(searchFile))
1091 ).map(path -> path.getFileName().toString()).collect(Collectors.toList());
1094 Set<String> namespaces = new LinkedHashSet<>();
1095 int fromIndex = baseNamespace != null ? baseNamespace.length() + 1 : 0;
1096 for (String namespace : allNamespaces) {
1097 if (baseNamespace == null || namespace.startsWith(baseNamespace + ".")) {
1098 int i = namespace.indexOf('.', fromIndex);
1100 namespaces.add(namespace.substring(fromIndex, i));
1102 namespaces.add(namespace.substring(fromIndex));
1106 return new ArrayList<>(namespaces);
1107 } catch (IOException e) {
1108 throw new MetadataResolutionException(e.getMessage(), e);
1113 public Collection<String> getProjects(RepositorySession session, String repoId, String namespace)
1114 throws MetadataResolutionException {
1116 List<String> projects;
1117 Path directory = getDirectory(repoId).resolve(namespace);
1118 if (!(Files.exists(directory) && Files.isDirectory(directory))) {
1119 return Collections.emptyList();
1121 final String searchFile = PROJECT_METADATA_KEY + ".properties";
1122 try (Stream<Path> fs = Files.list(directory)) {
1123 projects = fs.filter(Files::isDirectory).filter(path ->
1124 Files.exists(path.resolve(searchFile))
1125 ).map(path -> path.getFileName().toString()).collect(Collectors.toList());
1129 } catch (IOException e) {
1130 throw new MetadataResolutionException(e.getMessage(), e);
1135 public Collection<String> getProjectVersions(RepositorySession session, String repoId, String namespace, String projectId)
1136 throws MetadataResolutionException {
1138 List<String> projectVersions;
1139 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId);
1140 if (!(Files.exists(directory) && Files.isDirectory(directory))) {
1141 return Collections.emptyList();
1143 final String searchFile = PROJECT_VERSION_METADATA_KEY + ".properties";
1144 try (Stream<Path> fs = Files.list(directory)) {
1145 projectVersions = fs.filter(Files::isDirectory).filter(path ->
1146 Files.exists(path.resolve(searchFile))
1147 ).map(path -> path.getFileName().toString()).collect(Collectors.toList());
1149 return projectVersions;
1150 } catch (IOException e) {
1151 throw new MetadataResolutionException(e.getMessage(), e);
1156 public void removeProject(RepositorySession session, String repositoryId, String namespace, String projectId)
1157 throws MetadataRepositoryException {
1159 Path directory = getDirectory(repositoryId).resolve(namespace + "/" + projectId);
1160 org.apache.archiva.common.utils.FileUtils.deleteDirectory(directory);
1161 } catch (IOException e) {
1162 throw new MetadataRepositoryException(e.getMessage(), e);
1167 public void removeProjectVersion(RepositorySession session, String repoId, String namespace, String projectId, String projectVersion)
1168 throws MetadataRepositoryException {
1170 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
1171 org.apache.archiva.common.utils.FileUtils.deleteDirectory(directory);
1172 } catch (IOException e) {
1173 throw new MetadataRepositoryException(e.getMessage(), e);
1178 private void writeProperties(Properties properties, Path directory, String propertiesKey)
1179 throws IOException {
1180 Files.createDirectories(directory);
1181 try (OutputStream os = Files.newOutputStream(directory.resolve(propertiesKey + ".properties"))) {
1182 properties.store(os, null);
1186 private static class ArtifactComparator
1187 implements Comparator<ArtifactMetadata> {
1189 public int compare(ArtifactMetadata artifact1, ArtifactMetadata artifact2) {
1190 if (artifact1.getWhenGathered() == artifact2.getWhenGathered()) {
1193 if (artifact1.getWhenGathered() == null) {
1196 if (artifact2.getWhenGathered() == null) {
1199 return artifact1.getWhenGathered().compareTo(artifact2.getWhenGathered());
1204 public List<ArtifactMetadata> getArtifacts(RepositorySession session, String repoId)
1205 throws MetadataRepositoryException {
1207 List<ArtifactMetadata> artifacts = new ArrayList<>();
1208 for (String ns : getRootNamespaces(session, repoId)) {
1209 getArtifacts(session, artifacts, repoId, ns);
1212 } catch (MetadataResolutionException e) {
1213 throw new MetadataRepositoryException(e.getMessage(), e);
1217 private class ArtifactCoordinates {
1218 final String namespace;
1219 final String project;
1220 final String version;
1222 public ArtifactCoordinates(String namespace, String project, String version) {
1223 this.namespace = namespace;
1224 this.project = project;
1225 this.version = version;
1228 public String getNamespace( )
1233 public String getProject( )
1238 public String getVersion( )
1245 public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repositoryId,
1246 QueryParameter queryParameter ) throws MetadataResolutionException
1249 return getAllNamespacesStream( session, repositoryId ).filter( Objects::nonNull ).flatMap( ns ->
1253 return getProjects( session, repositoryId, ns ).stream( ).map( proj ->
1254 new ArtifactCoordinates( ns, proj, null ) );
1256 catch ( MetadataResolutionException e )
1261 ).filter( Objects::nonNull ).flatMap( artifactCoordinates ->
1265 return getProjectVersions( session, repositoryId, artifactCoordinates.getNamespace( ), artifactCoordinates.getProject( ) )
1266 .stream( ).map(version -> new ArtifactCoordinates( artifactCoordinates.getNamespace(), artifactCoordinates.getProject(), version ));
1268 catch ( MetadataResolutionException e )
1273 ).filter( Objects::nonNull ).flatMap( ac ->
1277 return getArtifactStream( session, repositoryId, ac.getNamespace(), ac.getProject(), ac.getVersion() );
1279 catch ( MetadataResolutionException e )
1284 ).filter( Objects::nonNull );
1288 public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repoId,
1289 final String namespace, final String projectId,
1290 final String projectVersion ) throws MetadataResolutionException
1292 return getArtifacts( session, repoId, namespace, projectId, projectVersion ).stream( );
1295 private void getArtifacts(RepositorySession session, List<ArtifactMetadata> artifacts, String repoId, String ns)
1296 throws MetadataResolutionException {
1297 for (String namespace : this.getChildNamespaces(session, repoId, ns)) {
1298 getArtifacts(session, artifacts, repoId, ns + "." + namespace);
1301 for (String project : getProjects(session, repoId, ns)) {
1302 for (String version : getProjectVersions(session, repoId, ns, project)) {
1303 artifacts.addAll(getArtifacts(session, repoId, ns, project, version));
1309 public List<ArtifactMetadata> searchArtifacts(RepositorySession session, String repositoryId, String text, boolean exact) {
1310 throw new UnsupportedOperationException("searchArtifacts not yet implemented in File backend");
1314 public List<ArtifactMetadata> searchArtifacts(RepositorySession session, String repositoryId, String key, String text, boolean exact) {
1315 throw new UnsupportedOperationException("searchArtifacts not yet implemented in File backend");