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.*;
27 import org.apache.archiva.metadata.repository.*;
28 import org.apache.commons.lang.StringUtils;
29 import org.slf4j.Logger;
30 import org.slf4j.LoggerFactory;
32 import javax.annotation.Nonnull;
33 import javax.annotation.Nullable;
34 import java.io.FileNotFoundException;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.io.OutputStream;
38 import java.nio.file.*;
39 import java.time.Instant;
40 import java.time.ZoneId;
41 import java.time.ZonedDateTime;
43 import java.util.stream.Collectors;
44 import java.util.stream.Stream;
46 public class FileMetadataRepository
47 extends AbstractMetadataRepository implements MetadataRepository {
49 private final ArchivaConfiguration configuration;
51 private Logger log = LoggerFactory.getLogger(FileMetadataRepository.class);
53 private static final String PROJECT_METADATA_KEY = "project-metadata";
55 private static final String PROJECT_VERSION_METADATA_KEY = "version-metadata";
57 private static final String NAMESPACE_METADATA_KEY = "namespace-metadata";
59 private static final String METADATA_KEY = "metadata";
61 private Map<String, Path> baseDirectory = new HashMap<>();
63 public FileMetadataRepository(MetadataService metadataService,
64 ArchivaConfiguration configuration) {
65 super(metadataService);
66 this.configuration = configuration;
69 private Path getBaseDirectory(String repoId)
71 if (!baseDirectory.containsKey(repoId)) {
73 ManagedRepositoryConfiguration managedRepositoryConfiguration =
74 configuration.getConfiguration().getManagedRepositoriesAsMap().get(repoId);
75 if (managedRepositoryConfiguration == null) {
76 baseDir = Files.createTempDirectory(repoId);
78 baseDir = Paths.get(managedRepositoryConfiguration.getLocation());
80 baseDirectory.put(repoId, baseDir.resolve(".archiva"));
82 return baseDirectory.get(repoId);
85 private Path getDirectory(String repoId)
87 return getBaseDirectory(repoId).resolve("content");
91 public void updateProject(RepositorySession session, String repoId, ProjectMetadata project) {
92 updateProject(session, repoId, project.getNamespace(), project.getId());
95 private void updateProject(RepositorySession session, String repoId, String namespace, String id) {
96 // TODO: this is a more braindead implementation than we would normally expect, for prototyping purposes
97 updateNamespace(session, repoId, namespace);
100 Path namespaceDirectory = getDirectory(repoId).resolve(namespace);
101 Properties properties = new Properties();
102 properties.setProperty("namespace", namespace);
103 properties.setProperty("id", id);
104 writeProperties(properties, namespaceDirectory.resolve(id), PROJECT_METADATA_KEY);
105 } catch (IOException e) {
106 log.error("Could not update project {}, {}, {}: {}", repoId, namespace, id, e.getMessage(), e);
111 public void updateProjectVersion(RepositorySession session, String repoId, String namespace, String projectId,
112 ProjectVersionMetadata versionMetadata) {
115 updateProject(session, repoId, namespace, projectId);
118 getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + versionMetadata.getId());
120 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
121 // remove properties that are not references or artifacts
122 for (Object key : new ArrayList<>(properties.keySet())) {
123 String name = (String) key;
124 if (!name.contains(":") && !name.equals("facetIds")) {
125 properties.remove(name);
128 // clear the facet contents so old properties are no longer written
129 clearMetadataFacetProperties(versionMetadata.getFacetList(), properties, "");
131 properties.setProperty("id", versionMetadata.getId());
132 setProperty(properties, "name", versionMetadata.getName());
133 setProperty(properties, "description", versionMetadata.getDescription());
134 setProperty(properties, "url", versionMetadata.getUrl());
135 setProperty(properties, "incomplete", String.valueOf(versionMetadata.isIncomplete()));
136 if (versionMetadata.getScm() != null) {
137 setProperty(properties, "scm.connection", versionMetadata.getScm().getConnection());
138 setProperty(properties, "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection());
139 setProperty(properties, "scm.url", versionMetadata.getScm().getUrl());
141 if (versionMetadata.getCiManagement() != null) {
142 setProperty(properties, "ci.system", versionMetadata.getCiManagement().getSystem());
143 setProperty(properties, "ci.url", versionMetadata.getCiManagement().getUrl());
145 if (versionMetadata.getIssueManagement() != null) {
146 setProperty(properties, "issue.system", versionMetadata.getIssueManagement().getSystem());
147 setProperty(properties, "issue.url", versionMetadata.getIssueManagement().getUrl());
149 if (versionMetadata.getOrganization() != null) {
150 setProperty(properties, "org.name", versionMetadata.getOrganization().getName());
151 setProperty(properties, "org.url", versionMetadata.getOrganization().getUrl());
154 for (License license : versionMetadata.getLicenses()) {
155 setProperty(properties, "license." + i + ".name", license.getName());
156 setProperty(properties, "license." + i + ".url", license.getUrl());
160 for (MailingList mailingList : versionMetadata.getMailingLists()) {
161 setProperty(properties, "mailingList." + i + ".archive", mailingList.getMainArchiveUrl());
162 setProperty(properties, "mailingList." + i + ".name", mailingList.getName());
163 setProperty(properties, "mailingList." + i + ".post", mailingList.getPostAddress());
164 setProperty(properties, "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress());
165 setProperty(properties, "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress());
166 setProperty(properties, "mailingList." + i + ".otherArchives",
167 join(mailingList.getOtherArchives()));
171 ProjectVersionReference reference = new ProjectVersionReference();
172 reference.setNamespace(namespace);
173 reference.setProjectId(projectId);
174 reference.setProjectVersion(versionMetadata.getId());
175 reference.setReferenceType(ProjectVersionReference.ReferenceType.DEPENDENCY);
176 for (Dependency dependency : versionMetadata.getDependencies()) {
177 setProperty(properties, "dependency." + i + ".classifier", dependency.getClassifier());
178 setProperty(properties, "dependency." + i + ".scope", dependency.getScope());
179 setProperty(properties, "dependency." + i + ".systemPath", dependency.getSystemPath());
180 setProperty(properties, "dependency." + i + ".artifactId", dependency.getArtifactId());
181 setProperty(properties, "dependency." + i + ".groupId", dependency.getGroupId());
182 setProperty(properties, "dependency." + i + ".version", dependency.getVersion());
183 setProperty(properties, "dependency." + i + ".type", dependency.getType());
184 setProperty(properties, "dependency." + i + ".optional", String.valueOf(dependency.isOptional()));
186 updateProjectReference(repoId, dependency.getGroupId(), dependency.getArtifactId(),
187 dependency.getVersion(), reference);
191 Set<String> facetIds = new LinkedHashSet<>(versionMetadata.getFacetIds());
192 facetIds.addAll(Arrays.asList(properties.getProperty("facetIds", "").split(",")));
193 properties.setProperty("facetIds", join(facetIds));
195 updateProjectVersionFacets(versionMetadata, properties);
197 writeProperties(properties, directory, PROJECT_VERSION_METADATA_KEY);
198 } catch (IOException e) {
199 log.error("Could not update project version {}, {}, {}: {}", repoId, namespace, versionMetadata.getId(), e.getMessage(), e);
203 private void updateProjectVersionFacets(ProjectVersionMetadata versionMetadata, Properties properties) {
204 for (MetadataFacet facet : versionMetadata.getFacetList()) {
205 for (Map.Entry<String, String> entry : facet.toProperties().entrySet()) {
206 properties.setProperty(facet.getFacetId() + ":" + entry.getKey(), entry.getValue());
211 private static void clearMetadataFacetProperties(Collection<MetadataFacet> facetList, Properties properties,
213 List<Object> propsToRemove = new ArrayList<>();
214 for (MetadataFacet facet : facetList) {
215 for (Object key : new ArrayList<>(properties.keySet())) {
216 String keyString = (String) key;
217 if (keyString.startsWith(prefix + facet.getFacetId() + ":")) {
218 propsToRemove.add(key);
223 for (Object key : propsToRemove) {
224 properties.remove(key);
228 private void updateProjectReference(String repoId, String namespace, String projectId, String projectVersion,
229 ProjectVersionReference reference) {
231 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
233 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
234 int i = Integer.parseInt(properties.getProperty("ref:lastReferenceNum", "-1")) + 1;
235 setProperty(properties, "ref:lastReferenceNum", Integer.toString(i));
236 setProperty(properties, "ref:reference." + i + ".namespace", reference.getNamespace());
237 setProperty(properties, "ref:reference." + i + ".projectId", reference.getProjectId());
238 setProperty(properties, "ref:reference." + i + ".projectVersion", reference.getProjectVersion());
239 setProperty(properties, "ref:reference." + i + ".referenceType", reference.getReferenceType().toString());
241 writeProperties(properties, directory, PROJECT_VERSION_METADATA_KEY);
242 } catch (IOException e) {
243 log.error("Could not update project reference {}, {}, {}, {}: {}", repoId, namespace, projectId, projectVersion, e.getMessage(), e);
248 public void updateNamespace(RepositorySession session, String repoId, String namespace) {
250 Path namespaceDirectory = getDirectory(repoId).resolve(namespace);
251 Properties properties = new Properties();
252 properties.setProperty("namespace", namespace);
253 writeProperties(properties, namespaceDirectory, NAMESPACE_METADATA_KEY);
255 } catch (IOException e) {
256 log.error("Could not update namespace of {}, {}: {}", repoId, namespace, e.getMessage(), e);
261 public List<String> getMetadataFacets(RepositorySession session, String repoId, String facetId)
262 throws MetadataRepositoryException {
264 Path directory = getMetadataDirectory(repoId, facetId);
265 if (!(Files.exists(directory) && Files.isDirectory(directory))) {
266 return Collections.emptyList();
269 final String searchFile = METADATA_KEY + ".properties";
270 try (Stream<Path> fs = Files.walk(directory, FileVisitOption.FOLLOW_LINKS)) {
271 facets = fs.filter(Files::isDirectory).filter(path -> Files.exists(path.resolve(searchFile)))
272 .map(path -> directory.relativize(path).toString()).collect(Collectors.toList());
275 } catch (IOException e) {
276 throw new MetadataRepositoryException(e.getMessage(), e);
281 public <T extends MetadataFacet> Stream<T> getMetadataFacetStream(RepositorySession session, String repositoryId, Class<T> facetClazz, QueryParameter queryParameter) throws MetadataRepositoryException {
282 final MetadataFacetFactory<T> metadataFacetFactory = getFacetFactory(facetClazz);
283 if (metadataFacetFactory == null) {
286 final String facetId = metadataFacetFactory.getFacetId();
287 final String searchFile = METADATA_KEY + ".properties";
289 Path directory = getMetadataDirectory(repositoryId, facetId);
290 return Files.walk(directory, FileVisitOption.FOLLOW_LINKS).filter(Files::isDirectory)
291 .filter(path -> Files.exists(path.resolve(searchFile)))
292 .map(path -> directory.relativize(path).toString())
294 .skip(queryParameter.getOffset())
295 .limit(queryParameter.getLimit())
296 .map(name -> getMetadataFacet(session, repositoryId, facetClazz, name));
297 } catch (IOException e) {
298 throw new MetadataRepositoryException(e.getMessage(), e);
303 public boolean hasMetadataFacet(RepositorySession session, String repositoryId, String facetId)
304 throws MetadataRepositoryException {
307 Path directory = getMetadataDirectory(repositoryId, facetId);
308 if (!(Files.exists(directory) && Files.isDirectory(directory))) {
311 final String searchFile = METADATA_KEY + ".properties";
312 try (Stream<Path> fs = Files.walk(directory, FileVisitOption.FOLLOW_LINKS)) {
313 return fs.filter(Files::isDirectory).anyMatch(path -> Files.exists(path.resolve(searchFile)));
315 } catch (IOException e) {
316 log.error("Could not retrieve facet metatadata {}, {}: {}", repositoryId, facetId, e.getMessage(), e);
317 throw new MetadataRepositoryException(e.getMessage(), e);
324 public <T extends MetadataFacet> T getMetadataFacet(RepositorySession session, String repositoryId, Class<T> facetClazz, String name) {
325 final MetadataFacetFactory<T> metadataFacetFactory = getFacetFactory(facetClazz);
326 if (metadataFacetFactory == null) {
329 final String facetId = metadataFacetFactory.getFacetId();
331 Properties properties;
334 readProperties(getMetadataDirectory(repositoryId, facetId).resolve(name), METADATA_KEY);
335 } catch (NoSuchFileException | FileNotFoundException e) {
337 } catch (IOException e) {
338 log.error("Could not read properties from {}, {}: {}", repositoryId, facetId, e.getMessage(), e);
341 T metadataFacet = null;
342 if (metadataFacetFactory != null) {
343 metadataFacet = metadataFacetFactory.createMetadataFacet(repositoryId, name);
344 Map<String, String> map = new HashMap<>();
345 for (Object key : new ArrayList<>(properties.keySet())) {
346 String property = (String) key;
347 map.put(property, properties.getProperty(property));
349 metadataFacet.fromProperties(map);
351 return metadataFacet;
356 public void addMetadataFacet(RepositorySession session, String repositoryId, MetadataFacet metadataFacet) {
357 Properties properties = new Properties();
358 properties.putAll(metadataFacet.toProperties());
362 getMetadataDirectory(repositoryId, metadataFacet.getFacetId()).resolve(metadataFacet.getName());
363 writeProperties(properties, directory, METADATA_KEY);
364 } catch (IOException e) {
366 log.error(e.getMessage(), e);
371 public void removeMetadataFacets(RepositorySession session, String repositoryId, String facetId)
372 throws MetadataRepositoryException {
374 Path dir = getMetadataDirectory(repositoryId, facetId);
375 org.apache.archiva.common.utils.FileUtils.deleteDirectory(dir);
376 } catch (IOException e) {
377 throw new MetadataRepositoryException(e.getMessage(), e);
382 public void removeMetadataFacet(RepositorySession session, String repoId, String facetId, String name)
383 throws MetadataRepositoryException {
385 Path dir = getMetadataDirectory(repoId, facetId).resolve(name);
386 org.apache.archiva.common.utils.FileUtils.deleteDirectory(dir);
387 } catch (IOException e) {
388 throw new MetadataRepositoryException(e.getMessage(), e);
393 public List<ArtifactMetadata> getArtifactsByDateRange(RepositorySession session, String repoId, ZonedDateTime startTime, ZonedDateTime endTime)
394 throws MetadataRepositoryException {
396 List<ArtifactMetadata> artifacts = new ArrayList<>();
397 for (String ns : getRootNamespaces(session, repoId)) {
398 getArtifactsByDateRange(session, artifacts, repoId, ns, startTime, endTime);
400 artifacts.sort(new ArtifactComparator());
402 } catch (MetadataResolutionException e) {
403 throw new MetadataRepositoryException(e.getMessage(), e);
409 * Result is sorted by date,
411 * @param session The repository session
412 * @param repositoryId The repository id
413 * @param startTime The start time, can be <code>null</code>
414 * @param endTime The end time, can be <code>null</code>
415 * @param queryParameter Additional parameters for the query that affect ordering and number of returned results.
417 * @throws MetadataRepositoryException
420 public Stream<ArtifactMetadata> getArtifactByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException {
422 List<ArtifactMetadata> artifacts = new ArrayList<>();
423 for (String ns : getRootNamespaces(session, repositoryId)) {
424 getArtifactsByDateRange(session, artifacts, repositoryId, ns, startTime, endTime);
426 Comparator<ArtifactMetadata> comp = getArtifactMetadataComparator(queryParameter, "whenGathered");
427 return artifacts.stream().sorted(comp).skip(queryParameter.getOffset()).limit(queryParameter.getLimit());
429 } catch (MetadataResolutionException e) {
430 throw new MetadataRepositoryException(e.getMessage(), e);
435 private void getArtifactsByDateRange(RepositorySession session, List<ArtifactMetadata> artifacts, String repoId, String ns, ZonedDateTime startTime,
436 ZonedDateTime endTime)
437 throws MetadataRepositoryException {
439 for (String namespace : getNamespaces(session, repoId, ns)) {
440 getArtifactsByDateRange(session, artifacts, repoId, ns + "." + namespace, startTime, endTime);
443 for (String project : getProjects(session, repoId, ns)) {
444 for (String version : getProjectVersions(session, repoId, ns, project)) {
445 for (ArtifactMetadata artifact : getArtifacts(session, repoId, ns, project, version)) {
446 if (startTime == null || startTime.isBefore(ZonedDateTime.from(artifact.getWhenGathered().toInstant().atZone(ZoneId.systemDefault())))) {
447 if (endTime == null || endTime.isAfter(ZonedDateTime.from(artifact.getWhenGathered().toInstant().atZone(ZoneId.systemDefault())))) {
448 artifacts.add(artifact);
454 } catch (MetadataResolutionException e) {
455 throw new MetadataRepositoryException(e.getMessage(), e);
461 public Collection<ArtifactMetadata> getArtifacts(RepositorySession session, String repoId, String namespace, String projectId,
462 String projectVersion)
463 throws MetadataResolutionException {
465 Map<String, ArtifactMetadata> artifacts = new HashMap<>();
467 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
469 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
471 for (Map.Entry entry : properties.entrySet()) {
472 String name = (String) entry.getKey();
473 StringTokenizer tok = new StringTokenizer(name, ":");
474 if (tok.hasMoreTokens() && "artifact".equals(tok.nextToken())) {
475 String field = tok.nextToken();
476 String id = tok.nextToken();
478 ArtifactMetadata artifact = artifacts.get(id);
479 if (artifact == null) {
480 artifact = new ArtifactMetadata();
481 artifact.setRepositoryId(repoId);
482 artifact.setNamespace(namespace);
483 artifact.setProject(projectId);
484 artifact.setProjectVersion(projectVersion);
485 artifact.setVersion(projectVersion);
487 artifacts.put(id, artifact);
490 String value = (String) entry.getValue();
491 if ("updated".equals(field)) {
492 artifact.setFileLastModified(Long.parseLong(value));
493 } else if ("size".equals(field)) {
494 artifact.setSize(Long.valueOf(value));
495 } else if ("whenGathered".equals(field)) {
496 artifact.setWhenGathered(ZonedDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(value)), ZoneId.of("GMT")));
497 } else if ("version".equals(field)) {
498 artifact.setVersion(value);
499 } else if (field.startsWith("checksum")) {
500 String algorithmStr = StringUtils.removeStart( name, "artifact:checksum:"+id+":");
501 artifact.setChecksum( ChecksumAlgorithm.valueOf( algorithmStr ), value );
502 } else if ("facetIds".equals(field)) {
503 if (value.length() > 0) {
504 String propertyPrefix = "artifact:facet:" + id + ":";
505 for (String facetId : value.split(",")) {
506 MetadataFacetFactory factory = getFacetFactory(facetId);
507 if (factory == null) {
508 log.error("Attempted to load unknown artifact metadata facet: {}", facetId);
510 MetadataFacet facet = factory.createMetadataFacet();
511 String prefix = propertyPrefix + facet.getFacetId();
512 Map<String, String> map = new HashMap<>();
513 for (Object key : new ArrayList<>(properties.keySet())) {
514 String property = (String) key;
515 if (property.startsWith(prefix)) {
516 map.put(property.substring(prefix.length() + 1),
517 properties.getProperty(property));
520 facet.fromProperties(map);
521 artifact.addFacet(facet);
526 updateArtifactFacets(artifact, properties);
530 return artifacts.values();
531 } catch (IOException e) {
532 throw new MetadataResolutionException(e.getMessage(), e);
538 public void close() {
539 // nothing additional to close
544 public boolean canObtainAccess(Class<?> aClass) {
549 public <T> T obtainAccess(RepositorySession session, Class<T> aClass) {
550 throw new IllegalArgumentException(
551 "Access using " + aClass + " is not supported on the file metadata storage");
554 private void updateArtifactFacets(ArtifactMetadata artifact, Properties properties) {
555 String propertyPrefix = "artifact:facet:" + artifact.getId() + ":";
556 for (MetadataFacet facet : artifact.getFacetList()) {
557 for (Map.Entry<String, String> e : facet.toProperties().entrySet()) {
558 String key = propertyPrefix + facet.getFacetId() + ":" + e.getKey();
559 properties.setProperty(key, e.getValue());
565 public List<ArtifactMetadata> getArtifactsByChecksum(RepositorySession session, String repositoryId, String checksum)
566 throws MetadataRepositoryException {
568 // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
569 // of this information (eg. in Lucene, as before)
570 // alternatively, we could build a referential tree in the content repository, however it would need some levels
571 // of depth to avoid being too broad to be useful (eg. /repository/checksums/a/ab/abcdef1234567)
573 return getArtifactStream( session, repositoryId ).filter(
574 a -> a.hasChecksum( checksum )
575 ).collect( Collectors.toList() );
576 } catch (MetadataResolutionException e) {
577 throw new MetadataRepositoryException(e.getMessage(), e);
582 public void removeNamespace(RepositorySession session, String repositoryId, String project)
583 throws MetadataRepositoryException {
585 Path namespaceDirectory = getDirectory(repositoryId).resolve(project);
586 org.apache.archiva.common.utils.FileUtils.deleteDirectory(namespaceDirectory);
587 //Properties properties = new Properties();
588 //properties.setProperty( "namespace", namespace );
589 //writeProperties( properties, namespaceDirectory, NAMESPACE_METADATA_KEY );
591 } catch (IOException e) {
592 throw new MetadataRepositoryException(e.getMessage(), e);
597 public void removeArtifact(RepositorySession session, ArtifactMetadata artifactMetadata, String baseVersion)
598 throws MetadataRepositoryException {
601 Path directory = getDirectory(artifactMetadata.getRepositoryId()).resolve(
602 artifactMetadata.getNamespace() + "/" + artifactMetadata.getProject() + "/"
605 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
607 String id = artifactMetadata.getId();
609 properties.remove("artifact:updated:" + id);
610 properties.remove("artifact:whenGathered:" + id);
611 properties.remove("artifact:size:" + id);
612 artifactMetadata.getChecksums().entrySet().stream().forEach( entry ->
613 properties.remove( "artifact:checksum:"+id+":"+entry.getKey().name() ));
614 properties.remove("artifact:version:" + id);
615 properties.remove("artifact:facetIds:" + id);
617 String prefix = "artifact:facet:" + id + ":";
618 for (Object key : new ArrayList<>(properties.keySet())) {
619 String property = (String) key;
620 if (property.startsWith(prefix)) {
621 properties.remove(property);
625 writeProperties(properties, directory, PROJECT_VERSION_METADATA_KEY);
626 } catch (IOException e) {
627 throw new MetadataRepositoryException(e.getMessage(), e);
633 public void removeArtifact(RepositorySession session, String repoId, String namespace, String project, String version, String id)
634 throws MetadataRepositoryException {
636 Path directory = getDirectory(repoId).resolve(namespace + "/" + project + "/" + version);
638 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
640 properties.remove("artifact:updated:" + id);
641 properties.remove("artifact:whenGathered:" + id);
642 properties.remove("artifact:size:" + id);
643 properties.remove("artifact:version:" + id);
644 properties.remove("artifact:facetIds:" + id);
646 String facetPrefix = "artifact:facet:" + id + ":";
647 String checksumPrefix = "artifact:checksum:"+id+":";
648 for (String property : properties.stringPropertyNames()) {
649 if (property.startsWith( checksumPrefix )) {
650 properties.remove( property );
651 } else if (property.startsWith(facetPrefix)) {
652 properties.remove(property);
656 org.apache.archiva.common.utils.FileUtils.deleteDirectory(directory);
657 //writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
658 } catch (IOException e) {
659 throw new MetadataRepositoryException(e.getMessage(), e);
664 * FIXME implements this !!!!
667 * @param repositoryId
670 * @param projectVersion
671 * @param metadataFacet will remove artifacts which have this {@link MetadataFacet} using equals
672 * @throws MetadataRepositoryException
675 public void removeArtifact(RepositorySession session, String repositoryId, String namespace, String project, String projectVersion,
676 MetadataFacet metadataFacet)
677 throws MetadataRepositoryException {
678 throw new UnsupportedOperationException("not implemented");
682 public void removeRepository(RepositorySession session, String repoId)
683 throws MetadataRepositoryException {
685 Path dir = getDirectory(repoId);
686 org.apache.archiva.common.utils.FileUtils.deleteDirectory(dir);
687 } catch (IOException e) {
688 throw new MetadataRepositoryException(e.getMessage(), e);
694 public List<ArtifactMetadata> getArtifactsByProjectVersionMetadata(RepositorySession session, String key, String value, String repositoryId)
695 throws MetadataRepositoryException {
696 throw new UnsupportedOperationException("not yet implemented in File backend");
700 public List<ArtifactMetadata> getArtifactsByMetadata(RepositorySession session, String key, String value, String repositoryId)
701 throws MetadataRepositoryException {
702 throw new UnsupportedOperationException("not yet implemented in File backend");
706 public List<ArtifactMetadata> getArtifactsByProperty(RepositorySession session, String key, String value, String repositoryId)
707 throws MetadataRepositoryException {
708 throw new UnsupportedOperationException("getArtifactsByProperty not yet implemented in File backend");
711 private Path getMetadataDirectory(String repoId, String facetId)
713 return getBaseDirectory(repoId).resolve("facets/" + facetId);
716 private String join(Collection<String> ids) {
717 if (ids != null && !ids.isEmpty()) {
718 StringBuilder s = new StringBuilder();
719 for (String id : ids) {
723 return s.substring(0, s.length() - 1);
728 private void setProperty(Properties properties, String name, String value) {
730 properties.setProperty(name, value);
735 public void updateArtifact(RepositorySession session, String repoId, String namespace, String projectId, String projectVersion,
736 ArtifactMetadata artifact) {
738 ProjectVersionMetadata metadata = new ProjectVersionMetadata();
739 metadata.setId(projectVersion);
740 updateProjectVersion(session, repoId, namespace, projectId, metadata);
742 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
744 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
746 clearMetadataFacetProperties(artifact.getFacetList(), properties,
747 "artifact:facet:" + artifact.getId() + ":");
749 String id = artifact.getId();
750 properties.setProperty("artifact:updated:" + id,
751 Long.toString(artifact.getFileLastModified().toInstant().toEpochMilli()));
752 properties.setProperty("artifact:whenGathered:" + id,
753 Long.toString(artifact.getWhenGathered().toInstant().toEpochMilli()));
754 properties.setProperty("artifact:size:" + id, Long.toString(artifact.getSize()));
755 artifact.getChecksums().entrySet().stream().forEach( entry ->
756 properties.setProperty( "artifact:checksum:"+id+":"+entry.getKey().name(), entry.getValue() ));
757 properties.setProperty("artifact:version:" + id, artifact.getVersion());
759 Set<String> facetIds = new LinkedHashSet<>(artifact.getFacetIds());
760 String property = "artifact:facetIds:" + id;
761 facetIds.addAll(Arrays.asList(properties.getProperty(property, "").split(",")));
762 properties.setProperty(property, join(facetIds));
764 updateArtifactFacets(artifact, properties);
766 writeProperties(properties, directory, PROJECT_VERSION_METADATA_KEY);
767 } catch (IOException e) {
769 log.error(e.getMessage(), e);
773 private Properties readOrCreateProperties(Path directory, String propertiesKey) {
775 return readProperties(directory, propertiesKey);
776 } catch (FileNotFoundException | NoSuchFileException e) {
777 // ignore and return new properties
778 } catch (IOException e) {
780 log.error(e.getMessage(), e);
782 return new Properties();
785 private Properties readProperties(Path directory, String propertiesKey)
787 Properties properties = new Properties();
788 try (InputStream in = Files.newInputStream(directory.resolve(propertiesKey + ".properties"))) {
796 public ProjectMetadata getProject(RepositorySession session, String repoId, String namespace, String projectId)
797 throws MetadataResolutionException {
799 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId);
801 Properties properties = readOrCreateProperties(directory, PROJECT_METADATA_KEY);
803 ProjectMetadata project = null;
805 String id = properties.getProperty("id");
807 project = new ProjectMetadata();
808 project.setNamespace(properties.getProperty("namespace"));
813 } catch (IOException e) {
814 throw new MetadataResolutionException(e.getMessage(), e);
819 public ProjectVersionMetadata getProjectVersion(RepositorySession session, String repoId, String namespace, String projectId,
820 String projectVersion)
821 throws MetadataResolutionException {
823 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
825 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
826 String id = properties.getProperty("id");
827 ProjectVersionMetadata versionMetadata = null;
829 versionMetadata = new ProjectVersionMetadata();
830 versionMetadata.setId(id);
831 versionMetadata.setName(properties.getProperty("name"));
832 versionMetadata.setDescription(properties.getProperty("description"));
833 versionMetadata.setUrl(properties.getProperty("url"));
834 versionMetadata.setIncomplete(Boolean.valueOf(properties.getProperty("incomplete", "false")));
836 String scmConnection = properties.getProperty("scm.connection");
837 String scmDeveloperConnection = properties.getProperty("scm.developerConnection");
838 String scmUrl = properties.getProperty("scm.url");
839 if (scmConnection != null || scmDeveloperConnection != null || scmUrl != null) {
841 scm.setConnection(scmConnection);
842 scm.setDeveloperConnection(scmDeveloperConnection);
844 versionMetadata.setScm(scm);
847 String ciSystem = properties.getProperty("ci.system");
848 String ciUrl = properties.getProperty("ci.url");
849 if (ciSystem != null || ciUrl != null) {
850 CiManagement ci = new CiManagement();
851 ci.setSystem(ciSystem);
853 versionMetadata.setCiManagement(ci);
856 String issueSystem = properties.getProperty("issue.system");
857 String issueUrl = properties.getProperty("issue.url");
858 if (issueSystem != null || issueUrl != null) {
859 IssueManagement issueManagement = new IssueManagement();
860 issueManagement.setSystem(issueSystem);
861 issueManagement.setUrl(issueUrl);
862 versionMetadata.setIssueManagement(issueManagement);
865 String orgName = properties.getProperty("org.name");
866 String orgUrl = properties.getProperty("org.url");
867 if (orgName != null || orgUrl != null) {
868 Organization org = new Organization();
869 org.setName(orgName);
871 versionMetadata.setOrganization(org);
874 boolean done = false;
877 String licenseName = properties.getProperty("license." + i + ".name");
878 String licenseUrl = properties.getProperty("license." + i + ".url");
879 if (licenseName != null || licenseUrl != null) {
880 License license = new License();
881 license.setName(licenseName);
882 license.setUrl(licenseUrl);
883 versionMetadata.addLicense(license);
893 String mailingListName = properties.getProperty("mailingList." + i + ".name");
894 if (mailingListName != null) {
895 MailingList mailingList = new MailingList();
896 mailingList.setName(mailingListName);
897 mailingList.setMainArchiveUrl(properties.getProperty("mailingList." + i + ".archive"));
898 String p = properties.getProperty("mailingList." + i + ".otherArchives");
899 if (p != null && p.length() > 0) {
900 mailingList.setOtherArchives(Arrays.asList(p.split(",")));
902 mailingList.setOtherArchives(Collections.emptyList());
904 mailingList.setPostAddress(properties.getProperty("mailingList." + i + ".post"));
905 mailingList.setSubscribeAddress(properties.getProperty("mailingList." + i + ".subscribe"));
906 mailingList.setUnsubscribeAddress(
907 properties.getProperty("mailingList." + i + ".unsubscribe"));
908 versionMetadata.addMailingList(mailingList);
918 String dependencyArtifactId = properties.getProperty("dependency." + i + ".artifactId");
919 if (dependencyArtifactId != null) {
920 Dependency dependency = new Dependency();
921 dependency.setArtifactId(dependencyArtifactId);
922 dependency.setGroupId(properties.getProperty("dependency." + i + ".groupId"));
923 dependency.setClassifier(properties.getProperty("dependency." + i + ".classifier"));
924 dependency.setOptional(
925 Boolean.valueOf(properties.getProperty("dependency." + i + ".optional")));
926 dependency.setScope(properties.getProperty("dependency." + i + ".scope"));
927 dependency.setSystemPath(properties.getProperty("dependency." + i + ".systemPath"));
928 dependency.setType(properties.getProperty("dependency." + i + ".type"));
929 dependency.setVersion(properties.getProperty("dependency." + i + ".version"));
930 dependency.setOptional(
931 Boolean.valueOf(properties.getProperty("dependency." + i + ".optional")));
932 versionMetadata.addDependency(dependency);
939 String facetIds = properties.getProperty("facetIds", "");
940 if (facetIds.length() > 0) {
941 for (String facetId : facetIds.split(",")) {
942 MetadataFacetFactory factory = getFacetFactory(facetId);
943 if (factory == null) {
944 log.error("Attempted to load unknown project version metadata facet: {}", facetId);
946 MetadataFacet facet = factory.createMetadataFacet();
947 Map<String, String> map = new HashMap<>();
948 for (Object key : new ArrayList<>(properties.keySet())) {
949 String property = (String) key;
950 if (property.startsWith(facet.getFacetId())) {
951 map.put(property.substring(facet.getFacetId().length() + 1),
952 properties.getProperty(property));
955 facet.fromProperties(map);
956 versionMetadata.addFacet(facet);
961 updateProjectVersionFacets(versionMetadata, properties);
963 return versionMetadata;
964 } catch (IOException e) {
965 throw new MetadataResolutionException(e.getMessage(), e);
970 public Collection<String> getArtifactVersions(RepositorySession session, String repoId, String namespace, String projectId,
971 String projectVersion)
972 throws MetadataResolutionException {
974 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
976 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
978 Set<String> versions = new HashSet<>();
979 for (Map.Entry entry : properties.entrySet()) {
980 String name = (String) entry.getKey();
981 if (name.startsWith("artifact:version:")) {
982 versions.add((String) entry.getValue());
986 } catch (IOException e) {
987 throw new MetadataResolutionException(e.getMessage(), e);
992 public Collection<ProjectVersionReference> getProjectReferences(RepositorySession session, String repoId, String namespace, String projectId,
993 String projectVersion)
994 throws MetadataResolutionException {
996 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
998 Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
999 int numberOfRefs = Integer.parseInt(properties.getProperty("ref:lastReferenceNum", "-1")) + 1;
1001 List<ProjectVersionReference> references = new ArrayList<>();
1002 for (int i = 0; i < numberOfRefs; i++) {
1003 ProjectVersionReference reference = new ProjectVersionReference();
1004 reference.setProjectId(properties.getProperty("ref:reference." + i + ".projectId"));
1005 reference.setNamespace(properties.getProperty("ref:reference." + i + ".namespace"));
1006 reference.setProjectVersion(properties.getProperty("ref:reference." + i + ".projectVersion"));
1007 reference.setReferenceType(ProjectVersionReference.ReferenceType.valueOf(
1008 properties.getProperty("ref:reference." + i + ".referenceType")));
1009 references.add(reference);
1012 } catch (IOException e) {
1013 throw new MetadataResolutionException(e.getMessage(), e);
1018 public Collection<String> getRootNamespaces(RepositorySession session, String repoId)
1019 throws MetadataResolutionException {
1020 return getNamespaces(session, repoId, null);
1023 private Stream<String> getAllNamespacesStream(RepositorySession session, String repoId) {
1024 Path directory = null;
1027 directory = getDirectory(repoId);
1029 catch ( IOException e )
1031 return Stream.empty( );
1033 if (!(Files.exists(directory) && Files.isDirectory(directory))) {
1034 return Stream.empty( );
1036 final String searchFile = NAMESPACE_METADATA_KEY + ".properties";
1039 return Files.list(directory).filter(Files::isDirectory).filter(path ->
1040 Files.exists(path.resolve(searchFile))
1041 ).map(path -> path.getFileName().toString());
1043 catch ( IOException e )
1045 return Stream.empty( );
1050 public Collection<String> getNamespaces(RepositorySession session, String repoId, String baseNamespace)
1051 throws MetadataResolutionException {
1053 List<String> allNamespaces;
1054 Path directory = getDirectory(repoId);
1055 if (!(Files.exists(directory) && Files.isDirectory(directory))) {
1056 return Collections.emptyList();
1058 final String searchFile = NAMESPACE_METADATA_KEY + ".properties";
1059 try (Stream<Path> fs = Files.list(directory)) {
1060 allNamespaces = fs.filter(Files::isDirectory).filter(path ->
1061 Files.exists(path.resolve(searchFile))
1062 ).map(path -> path.getFileName().toString()).collect(Collectors.toList());
1065 Set<String> namespaces = new LinkedHashSet<>();
1066 int fromIndex = baseNamespace != null ? baseNamespace.length() + 1 : 0;
1067 for (String namespace : allNamespaces) {
1068 if (baseNamespace == null || namespace.startsWith(baseNamespace + ".")) {
1069 int i = namespace.indexOf('.', fromIndex);
1071 namespaces.add(namespace.substring(fromIndex, i));
1073 namespaces.add(namespace.substring(fromIndex));
1077 return new ArrayList<>(namespaces);
1078 } catch (IOException e) {
1079 throw new MetadataResolutionException(e.getMessage(), e);
1084 public Collection<String> getProjects(RepositorySession session, String repoId, String namespace)
1085 throws MetadataResolutionException {
1087 List<String> projects;
1088 Path directory = getDirectory(repoId).resolve(namespace);
1089 if (!(Files.exists(directory) && Files.isDirectory(directory))) {
1090 return Collections.emptyList();
1092 final String searchFile = PROJECT_METADATA_KEY + ".properties";
1093 try (Stream<Path> fs = Files.list(directory)) {
1094 projects = fs.filter(Files::isDirectory).filter(path ->
1095 Files.exists(path.resolve(searchFile))
1096 ).map(path -> path.getFileName().toString()).collect(Collectors.toList());
1100 } catch (IOException e) {
1101 throw new MetadataResolutionException(e.getMessage(), e);
1106 public Collection<String> getProjectVersions(RepositorySession session, String repoId, String namespace, String projectId)
1107 throws MetadataResolutionException {
1109 List<String> projectVersions;
1110 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId);
1111 if (!(Files.exists(directory) && Files.isDirectory(directory))) {
1112 return Collections.emptyList();
1114 final String searchFile = PROJECT_VERSION_METADATA_KEY + ".properties";
1115 try (Stream<Path> fs = Files.list(directory)) {
1116 projectVersions = fs.filter(Files::isDirectory).filter(path ->
1117 Files.exists(path.resolve(searchFile))
1118 ).map(path -> path.getFileName().toString()).collect(Collectors.toList());
1120 return projectVersions;
1121 } catch (IOException e) {
1122 throw new MetadataResolutionException(e.getMessage(), e);
1127 public void removeProject(RepositorySession session, String repositoryId, String namespace, String projectId)
1128 throws MetadataRepositoryException {
1130 Path directory = getDirectory(repositoryId).resolve(namespace + "/" + projectId);
1131 org.apache.archiva.common.utils.FileUtils.deleteDirectory(directory);
1132 } catch (IOException e) {
1133 throw new MetadataRepositoryException(e.getMessage(), e);
1138 public void removeProjectVersion(RepositorySession session, String repoId, String namespace, String projectId, String projectVersion)
1139 throws MetadataRepositoryException {
1141 Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
1142 org.apache.archiva.common.utils.FileUtils.deleteDirectory(directory);
1143 } catch (IOException e) {
1144 throw new MetadataRepositoryException(e.getMessage(), e);
1149 private void writeProperties(Properties properties, Path directory, String propertiesKey)
1150 throws IOException {
1151 Files.createDirectories(directory);
1152 try (OutputStream os = Files.newOutputStream(directory.resolve(propertiesKey + ".properties"))) {
1153 properties.store(os, null);
1157 private static class ArtifactComparator
1158 implements Comparator<ArtifactMetadata> {
1160 public int compare(ArtifactMetadata artifact1, ArtifactMetadata artifact2) {
1161 if (artifact1.getWhenGathered() == artifact2.getWhenGathered()) {
1164 if (artifact1.getWhenGathered() == null) {
1167 if (artifact2.getWhenGathered() == null) {
1170 return artifact1.getWhenGathered().compareTo(artifact2.getWhenGathered());
1175 public List<ArtifactMetadata> getArtifacts(RepositorySession session, String repoId)
1176 throws MetadataRepositoryException {
1178 List<ArtifactMetadata> artifacts = new ArrayList<>();
1179 for (String ns : getRootNamespaces(session, repoId)) {
1180 getArtifacts(session, artifacts, repoId, ns);
1183 } catch (MetadataResolutionException e) {
1184 throw new MetadataRepositoryException(e.getMessage(), e);
1188 private class ArtifactCoordinates {
1189 final String namespace;
1190 final String project;
1191 final String version;
1193 public ArtifactCoordinates(String namespace, String project, String version) {
1194 this.namespace = namespace;
1195 this.project = project;
1196 this.version = version;
1199 public String getNamespace( )
1204 public String getProject( )
1209 public String getVersion( )
1216 public Stream<ArtifactMetadata> getArtifactStream( @Nonnull final RepositorySession session, @Nonnull final String repositoryId,
1217 @Nonnull QueryParameter queryParameter ) throws MetadataResolutionException
1220 return getAllNamespacesStream( session, repositoryId ).filter( Objects::nonNull ).flatMap( ns ->
1224 return getProjects( session, repositoryId, ns ).stream( ).map( proj ->
1225 new ArtifactCoordinates( ns, proj, null ) );
1227 catch ( MetadataResolutionException e )
1232 ).filter( Objects::nonNull ).flatMap( artifactCoordinates ->
1236 return getProjectVersions( session, repositoryId, artifactCoordinates.getNamespace( ), artifactCoordinates.getProject( ) )
1237 .stream( ).map(version -> new ArtifactCoordinates( artifactCoordinates.getNamespace(), artifactCoordinates.getProject(), version ));
1239 catch ( MetadataResolutionException e )
1244 ).filter( Objects::nonNull ).flatMap( ac ->
1248 return getArtifactStream( session, repositoryId, ac.getNamespace(), ac.getProject(), ac.getVersion() );
1250 catch ( MetadataResolutionException e )
1255 ).filter( Objects::nonNull );
1259 public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repoId,
1260 final String namespace, final String projectId,
1261 final String projectVersion ) throws MetadataResolutionException
1263 return getArtifacts( session, repoId, namespace, projectId, projectVersion ).stream( );
1266 private void getArtifacts(RepositorySession session, List<ArtifactMetadata> artifacts, String repoId, String ns)
1267 throws MetadataResolutionException {
1268 for (String namespace : getNamespaces(session, repoId, ns)) {
1269 getArtifacts(session, artifacts, repoId, ns + "." + namespace);
1272 for (String project : getProjects(session, repoId, ns)) {
1273 for (String version : getProjectVersions(session, repoId, ns, project)) {
1274 artifacts.addAll(getArtifacts(session, repoId, ns, project, version));
1280 public List<ArtifactMetadata> searchArtifacts(RepositorySession session, String repositoryId, String text, boolean exact) {
1281 throw new UnsupportedOperationException("searchArtifacts not yet implemented in File backend");
1285 public List<ArtifactMetadata> searchArtifacts(RepositorySession session, String repositoryId, String key, String text, boolean exact) {
1286 throw new UnsupportedOperationException("searchArtifacts not yet implemented in File backend");