]> source.dussan.org Git - archiva.git/blob
4719d92a04897e058ed23e3ab5496a0986683dc6
[archiva.git] /
1 package org.apache.archiva.metadata.repository.file;
2
3 /*
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
11  *
12  *   http://www.apache.org/licenses/LICENSE-2.0
13  *
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
19  * under the License.
20  */
21
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;
31
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;
42 import java.util.*;
43 import java.util.stream.Collectors;
44 import java.util.stream.Stream;
45
46 public class FileMetadataRepository
47         extends AbstractMetadataRepository implements MetadataRepository {
48
49     private final ArchivaConfiguration configuration;
50
51     private Logger log = LoggerFactory.getLogger(FileMetadataRepository.class);
52
53     private static final String PROJECT_METADATA_KEY = "project-metadata";
54
55     private static final String PROJECT_VERSION_METADATA_KEY = "version-metadata";
56
57     private static final String NAMESPACE_METADATA_KEY = "namespace-metadata";
58
59     private static final String METADATA_KEY = "metadata";
60
61     private Map<String, Path> baseDirectory = new HashMap<>();
62
63     public FileMetadataRepository(MetadataService metadataService,
64                                   ArchivaConfiguration configuration) {
65         super(metadataService);
66         this.configuration = configuration;
67     }
68
69     private Path getBaseDirectory(String repoId)
70             throws IOException {
71         if (!baseDirectory.containsKey(repoId)) {
72             Path baseDir;
73             ManagedRepositoryConfiguration managedRepositoryConfiguration =
74                     configuration.getConfiguration().getManagedRepositoriesAsMap().get(repoId);
75             if (managedRepositoryConfiguration == null) {
76                 baseDir = Files.createTempDirectory(repoId);
77             } else {
78                 baseDir = Paths.get(managedRepositoryConfiguration.getLocation());
79             }
80             baseDirectory.put(repoId, baseDir.resolve(".archiva"));
81         }
82         return baseDirectory.get(repoId);
83     }
84
85     private Path getDirectory(String repoId)
86             throws IOException {
87         return getBaseDirectory(repoId).resolve("content");
88     }
89
90     @Override
91     public void updateProject(RepositorySession session, String repoId, ProjectMetadata project) {
92         updateProject(session, repoId, project.getNamespace(), project.getId());
93     }
94
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);
98
99         try {
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);
107         }
108     }
109
110     @Override
111     public void updateProjectVersion(RepositorySession session, String repoId, String namespace, String projectId,
112                                      ProjectVersionMetadata versionMetadata) {
113
114         try {
115             updateProject(session, repoId, namespace, projectId);
116
117             Path directory =
118                     getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + versionMetadata.getId());
119
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);
126                 }
127
128                 // clear the facet contents so old properties are no longer written
129                 clearMetadataFacetProperties(versionMetadata.getFacetList(), properties, "");
130             }
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());
140             }
141             if (versionMetadata.getCiManagement() != null) {
142                 setProperty(properties, "ci.system", versionMetadata.getCiManagement().getSystem());
143                 setProperty(properties, "ci.url", versionMetadata.getCiManagement().getUrl());
144             }
145             if (versionMetadata.getIssueManagement() != null) {
146                 setProperty(properties, "issue.system", versionMetadata.getIssueManagement().getSystem());
147                 setProperty(properties, "issue.url", versionMetadata.getIssueManagement().getUrl());
148             }
149             if (versionMetadata.getOrganization() != null) {
150                 setProperty(properties, "org.name", versionMetadata.getOrganization().getName());
151                 setProperty(properties, "org.url", versionMetadata.getOrganization().getUrl());
152             }
153             int i = 0;
154             for (License license : versionMetadata.getLicenses()) {
155                 setProperty(properties, "license." + i + ".name", license.getName());
156                 setProperty(properties, "license." + i + ".url", license.getUrl());
157                 i++;
158             }
159             i = 0;
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()));
168                 i++;
169             }
170             i = 0;
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()));
185
186                 updateProjectReference(repoId, dependency.getGroupId(), dependency.getArtifactId(),
187                         dependency.getVersion(), reference);
188
189                 i++;
190             }
191             Set<String> facetIds = new LinkedHashSet<>(versionMetadata.getFacetIds());
192             facetIds.addAll(Arrays.asList(properties.getProperty("facetIds", "").split(",")));
193             properties.setProperty("facetIds", join(facetIds));
194
195             updateProjectVersionFacets(versionMetadata, properties);
196
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);
200         }
201     }
202
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());
207             }
208         }
209     }
210
211     private static void clearMetadataFacetProperties(Collection<MetadataFacet> facetList, Properties properties,
212                                                      String prefix) {
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);
219                 }
220             }
221         }
222
223         for (Object key : propsToRemove) {
224             properties.remove(key);
225         }
226     }
227
228     private void updateProjectReference(String repoId, String namespace, String projectId, String projectVersion,
229                                         ProjectVersionReference reference) {
230         try {
231             Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
232
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());
240
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);
244         }
245     }
246
247     @Override
248     public void updateNamespace(RepositorySession session, String repoId, String namespace) {
249         try {
250             Path namespaceDirectory = getDirectory(repoId).resolve(namespace);
251             Properties properties = new Properties();
252             properties.setProperty("namespace", namespace);
253             writeProperties(properties, namespaceDirectory, NAMESPACE_METADATA_KEY);
254
255         } catch (IOException e) {
256             log.error("Could not update namespace of {}, {}: {}", repoId, namespace, e.getMessage(), e);
257         }
258     }
259
260     @Override
261     public List<String> getMetadataFacets(RepositorySession session, String repoId, String facetId)
262             throws MetadataRepositoryException {
263         try {
264             Path directory = getMetadataDirectory(repoId, facetId);
265             if (!(Files.exists(directory) && Files.isDirectory(directory))) {
266                 return Collections.emptyList();
267             }
268             List<String> facets;
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());
273             }
274             return facets;
275         } catch (IOException e) {
276             throw new MetadataRepositoryException(e.getMessage(), e);
277         }
278     }
279
280     @Override
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) {
284             return null;
285         }
286         final String facetId = metadataFacetFactory.getFacetId();
287         final String searchFile = METADATA_KEY + ".properties";
288         try {
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())
293                     .sorted()
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);
299         }
300     }
301
302     @Override
303     public boolean hasMetadataFacet(RepositorySession session, String repositoryId, String facetId)
304             throws MetadataRepositoryException {
305
306         try {
307             Path directory = getMetadataDirectory(repositoryId, facetId);
308             if (!(Files.exists(directory) && Files.isDirectory(directory))) {
309                 return false;
310             }
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)));
314             }
315         } catch (IOException e) {
316             log.error("Could not retrieve facet metatadata {}, {}: {}", repositoryId, facetId, e.getMessage(), e);
317             throw new MetadataRepositoryException(e.getMessage(), e);
318         }
319
320     }
321
322
323     @Override
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) {
327             return null;
328         }
329         final String facetId = metadataFacetFactory.getFacetId();
330
331         Properties properties;
332         try {
333             properties =
334                     readProperties(getMetadataDirectory(repositoryId, facetId).resolve(name), METADATA_KEY);
335         } catch (NoSuchFileException | FileNotFoundException e) {
336             return null;
337         } catch (IOException e) {
338             log.error("Could not read properties from {}, {}: {}", repositoryId, facetId, e.getMessage(), e);
339             return null;
340         }
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));
348             }
349             metadataFacet.fromProperties(map);
350         }
351         return metadataFacet;
352     }
353
354
355     @Override
356     public void addMetadataFacet(RepositorySession session, String repositoryId, MetadataFacet metadataFacet) {
357         Properties properties = new Properties();
358         properties.putAll(metadataFacet.toProperties());
359
360         try {
361             Path directory =
362                     getMetadataDirectory(repositoryId, metadataFacet.getFacetId()).resolve(metadataFacet.getName());
363             writeProperties(properties, directory, METADATA_KEY);
364         } catch (IOException e) {
365             // TODO!
366             log.error(e.getMessage(), e);
367         }
368     }
369
370     @Override
371     public void removeMetadataFacets(RepositorySession session, String repositoryId, String facetId)
372             throws MetadataRepositoryException {
373         try {
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);
378         }
379     }
380
381     @Override
382     public void removeMetadataFacet(RepositorySession session, String repoId, String facetId, String name)
383             throws MetadataRepositoryException {
384         try {
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);
389         }
390     }
391
392     @Override
393     public List<ArtifactMetadata> getArtifactsByDateRange(RepositorySession session, String repoId, ZonedDateTime startTime, ZonedDateTime endTime)
394             throws MetadataRepositoryException {
395         try {
396             List<ArtifactMetadata> artifacts = new ArrayList<>();
397             for (String ns : getRootNamespaces(session, repoId)) {
398                 getArtifactsByDateRange(session, artifacts, repoId, ns, startTime, endTime);
399             }
400             artifacts.sort(new ArtifactComparator());
401             return artifacts;
402         } catch (MetadataResolutionException e) {
403             throw new MetadataRepositoryException(e.getMessage(), e);
404         }
405     }
406
407
408     /**
409      * Result is sorted by date,
410      *
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.
416      * @return
417      * @throws MetadataRepositoryException
418      */
419     @Override
420     public Stream<ArtifactMetadata> getArtifactByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, QueryParameter queryParameter) throws MetadataRepositoryException {
421         try {
422             List<ArtifactMetadata> artifacts = new ArrayList<>();
423             for (String ns : getRootNamespaces(session, repositoryId)) {
424                 getArtifactsByDateRange(session, artifacts, repositoryId, ns, startTime, endTime);
425             }
426             Comparator<ArtifactMetadata> comp = getArtifactMetadataComparator(queryParameter, "whenGathered");
427             return artifacts.stream().sorted(comp).skip(queryParameter.getOffset()).limit(queryParameter.getLimit());
428
429         } catch (MetadataResolutionException e) {
430             throw new MetadataRepositoryException(e.getMessage(), e);
431         }
432     }
433
434
435     private void getArtifactsByDateRange(RepositorySession session, List<ArtifactMetadata> artifacts, String repoId, String ns, ZonedDateTime startTime,
436                                          ZonedDateTime endTime)
437             throws MetadataRepositoryException {
438         try {
439             for (String namespace : getNamespaces(session, repoId, ns)) {
440                 getArtifactsByDateRange(session, artifacts, repoId, ns + "." + namespace, startTime, endTime);
441             }
442
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);
449                             }
450                         }
451                     }
452                 }
453             }
454         } catch (MetadataResolutionException e) {
455             throw new MetadataRepositoryException(e.getMessage(), e);
456         }
457     }
458
459
460     @Override
461     public Collection<ArtifactMetadata> getArtifacts(RepositorySession session, String repoId, String namespace, String projectId,
462                                                      String projectVersion)
463             throws MetadataResolutionException {
464         try {
465             Map<String, ArtifactMetadata> artifacts = new HashMap<>();
466
467             Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
468
469             Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
470
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();
477
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);
486                         artifact.setId(id);
487                         artifacts.put(id, artifact);
488                     }
489
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);
509                                 } else {
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));
518                                         }
519                                     }
520                                     facet.fromProperties(map);
521                                     artifact.addFacet(facet);
522                                 }
523                             }
524                         }
525
526                         updateArtifactFacets(artifact, properties);
527                     }
528                 }
529             }
530             return artifacts.values();
531         } catch (IOException e) {
532             throw new MetadataResolutionException(e.getMessage(), e);
533         }
534     }
535
536
537     @Override
538     public void close() {
539         // nothing additional to close
540     }
541
542
543     @Override
544     public boolean canObtainAccess(Class<?> aClass) {
545         return false;
546     }
547
548     @Override
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");
552     }
553
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());
560             }
561         }
562     }
563
564     @Override
565     public List<ArtifactMetadata> getArtifactsByChecksum(RepositorySession session, String repositoryId, String checksum)
566             throws MetadataRepositoryException {
567         try {
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)
572
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);
578         }
579     }
580
581     @Override
582     public void removeNamespace(RepositorySession session, String repositoryId, String project)
583             throws MetadataRepositoryException {
584         try {
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 );
590
591         } catch (IOException e) {
592             throw new MetadataRepositoryException(e.getMessage(), e);
593         }
594     }
595
596     @Override
597     public void removeArtifact(RepositorySession session, ArtifactMetadata artifactMetadata, String baseVersion)
598             throws MetadataRepositoryException {
599
600         try {
601             Path directory = getDirectory(artifactMetadata.getRepositoryId()).resolve(
602                     artifactMetadata.getNamespace() + "/" + artifactMetadata.getProject() + "/"
603                             + baseVersion);
604
605             Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
606
607             String id = artifactMetadata.getId();
608
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);
616
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);
622                 }
623             }
624
625             writeProperties(properties, directory, PROJECT_VERSION_METADATA_KEY);
626         } catch (IOException e) {
627             throw new MetadataRepositoryException(e.getMessage(), e);
628         }
629
630     }
631
632     @Override
633     public void removeArtifact(RepositorySession session, String repoId, String namespace, String project, String version, String id)
634             throws MetadataRepositoryException {
635         try {
636             Path directory = getDirectory(repoId).resolve(namespace + "/" + project + "/" + version);
637
638             Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
639
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);
645
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);
653                 }
654             }
655
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);
660         }
661     }
662
663     /**
664      * FIXME implements this !!!!
665      *
666      * @param session
667      * @param repositoryId
668      * @param namespace
669      * @param project
670      * @param projectVersion
671      * @param metadataFacet  will remove artifacts which have this {@link MetadataFacet} using equals
672      * @throws MetadataRepositoryException
673      */
674     @Override
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");
679     }
680
681     @Override
682     public void removeRepository(RepositorySession session, String repoId)
683             throws MetadataRepositoryException {
684         try {
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);
689         }
690     }
691
692
693     @Override
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");
697     }
698
699     @Override
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");
703     }
704
705     @Override
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");
709     }
710
711     private Path getMetadataDirectory(String repoId, String facetId)
712             throws IOException {
713         return getBaseDirectory(repoId).resolve("facets/" + facetId);
714     }
715
716     private String join(Collection<String> ids) {
717         if (ids != null && !ids.isEmpty()) {
718             StringBuilder s = new StringBuilder();
719             for (String id : ids) {
720                 s.append(id);
721                 s.append(",");
722             }
723             return s.substring(0, s.length() - 1);
724         }
725         return "";
726     }
727
728     private void setProperty(Properties properties, String name, String value) {
729         if (value != null) {
730             properties.setProperty(name, value);
731         }
732     }
733
734     @Override
735     public void updateArtifact(RepositorySession session, String repoId, String namespace, String projectId, String projectVersion,
736                                ArtifactMetadata artifact) {
737         try {
738             ProjectVersionMetadata metadata = new ProjectVersionMetadata();
739             metadata.setId(projectVersion);
740             updateProjectVersion(session, repoId, namespace, projectId, metadata);
741
742             Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
743
744             Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
745
746             clearMetadataFacetProperties(artifact.getFacetList(), properties,
747                     "artifact:facet:" + artifact.getId() + ":");
748
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());
758
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));
763
764             updateArtifactFacets(artifact, properties);
765
766             writeProperties(properties, directory, PROJECT_VERSION_METADATA_KEY);
767         } catch (IOException e) {
768             // TODO
769             log.error(e.getMessage(), e);
770         }
771     }
772
773     private Properties readOrCreateProperties(Path directory, String propertiesKey) {
774         try {
775             return readProperties(directory, propertiesKey);
776         } catch (FileNotFoundException | NoSuchFileException e) {
777             // ignore and return new properties
778         } catch (IOException e) {
779             // TODO
780             log.error(e.getMessage(), e);
781         }
782         return new Properties();
783     }
784
785     private Properties readProperties(Path directory, String propertiesKey)
786             throws IOException {
787         Properties properties = new Properties();
788         try (InputStream in = Files.newInputStream(directory.resolve(propertiesKey + ".properties"))) {
789
790             properties.load(in);
791         }
792         return properties;
793     }
794
795     @Override
796     public ProjectMetadata getProject(RepositorySession session, String repoId, String namespace, String projectId)
797             throws MetadataResolutionException {
798         try {
799             Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId);
800
801             Properties properties = readOrCreateProperties(directory, PROJECT_METADATA_KEY);
802
803             ProjectMetadata project = null;
804
805             String id = properties.getProperty("id");
806             if (id != null) {
807                 project = new ProjectMetadata();
808                 project.setNamespace(properties.getProperty("namespace"));
809                 project.setId(id);
810             }
811
812             return project;
813         } catch (IOException e) {
814             throw new MetadataResolutionException(e.getMessage(), e);
815         }
816     }
817
818     @Override
819     public ProjectVersionMetadata getProjectVersion(RepositorySession session, String repoId, String namespace, String projectId,
820                                                     String projectVersion)
821             throws MetadataResolutionException {
822         try {
823             Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
824
825             Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
826             String id = properties.getProperty("id");
827             ProjectVersionMetadata versionMetadata = null;
828             if (id != 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")));
835
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) {
840                     Scm scm = new Scm();
841                     scm.setConnection(scmConnection);
842                     scm.setDeveloperConnection(scmDeveloperConnection);
843                     scm.setUrl(scmUrl);
844                     versionMetadata.setScm(scm);
845                 }
846
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);
852                     ci.setUrl(ciUrl);
853                     versionMetadata.setCiManagement(ci);
854                 }
855
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);
863                 }
864
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);
870                     org.setUrl(orgUrl);
871                     versionMetadata.setOrganization(org);
872                 }
873
874                 boolean done = false;
875                 int i = 0;
876                 while (!done) {
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);
884                     } else {
885                         done = true;
886                     }
887                     i++;
888                 }
889
890                 done = false;
891                 i = 0;
892                 while (!done) {
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(",")));
901                         } else {
902                             mailingList.setOtherArchives(Collections.emptyList());
903                         }
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);
909                     } else {
910                         done = true;
911                     }
912                     i++;
913                 }
914
915                 done = false;
916                 i = 0;
917                 while (!done) {
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);
933                     } else {
934                         done = true;
935                     }
936                     i++;
937                 }
938
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);
945                         } else {
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));
953                                 }
954                             }
955                             facet.fromProperties(map);
956                             versionMetadata.addFacet(facet);
957                         }
958                     }
959                 }
960
961                 updateProjectVersionFacets(versionMetadata, properties);
962             }
963             return versionMetadata;
964         } catch (IOException e) {
965             throw new MetadataResolutionException(e.getMessage(), e);
966         }
967     }
968
969     @Override
970     public Collection<String> getArtifactVersions(RepositorySession session, String repoId, String namespace, String projectId,
971                                                   String projectVersion)
972             throws MetadataResolutionException {
973         try {
974             Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
975
976             Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
977
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());
983                 }
984             }
985             return versions;
986         } catch (IOException e) {
987             throw new MetadataResolutionException(e.getMessage(), e);
988         }
989     }
990
991     @Override
992     public Collection<ProjectVersionReference> getProjectReferences(RepositorySession session, String repoId, String namespace, String projectId,
993                                                                     String projectVersion)
994             throws MetadataResolutionException {
995         try {
996             Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId + "/" + projectVersion);
997
998             Properties properties = readOrCreateProperties(directory, PROJECT_VERSION_METADATA_KEY);
999             int numberOfRefs = Integer.parseInt(properties.getProperty("ref:lastReferenceNum", "-1")) + 1;
1000
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);
1010             }
1011             return references;
1012         } catch (IOException e) {
1013             throw new MetadataResolutionException(e.getMessage(), e);
1014         }
1015     }
1016
1017     @Override
1018     public Collection<String> getRootNamespaces(RepositorySession session, String repoId)
1019             throws MetadataResolutionException {
1020         return getNamespaces(session, repoId, null);
1021     }
1022
1023     private Stream<String> getAllNamespacesStream(RepositorySession session, String repoId) {
1024         Path directory = null;
1025         try
1026         {
1027             directory = getDirectory(repoId);
1028         }
1029         catch ( IOException e )
1030         {
1031             return Stream.empty( );
1032         }
1033         if (!(Files.exists(directory) && Files.isDirectory(directory))) {
1034             return Stream.empty( );
1035         }
1036         final String searchFile = NAMESPACE_METADATA_KEY + ".properties";
1037         try
1038         {
1039             return  Files.list(directory).filter(Files::isDirectory).filter(path ->
1040                     Files.exists(path.resolve(searchFile))
1041                 ).map(path -> path.getFileName().toString());
1042         }
1043         catch ( IOException e )
1044         {
1045             return Stream.empty( );
1046         }
1047     }
1048
1049     @Override
1050     public Collection<String> getNamespaces(RepositorySession session, String repoId, String baseNamespace)
1051             throws MetadataResolutionException {
1052         try {
1053             List<String> allNamespaces;
1054             Path directory = getDirectory(repoId);
1055             if (!(Files.exists(directory) && Files.isDirectory(directory))) {
1056                 return Collections.emptyList();
1057             }
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());
1063             }
1064
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);
1070                     if (i >= 0) {
1071                         namespaces.add(namespace.substring(fromIndex, i));
1072                     } else {
1073                         namespaces.add(namespace.substring(fromIndex));
1074                     }
1075                 }
1076             }
1077             return new ArrayList<>(namespaces);
1078         } catch (IOException e) {
1079             throw new MetadataResolutionException(e.getMessage(), e);
1080         }
1081     }
1082
1083     @Override
1084     public Collection<String> getProjects(RepositorySession session, String repoId, String namespace)
1085             throws MetadataResolutionException {
1086         try {
1087             List<String> projects;
1088             Path directory = getDirectory(repoId).resolve(namespace);
1089             if (!(Files.exists(directory) && Files.isDirectory(directory))) {
1090                 return Collections.emptyList();
1091             }
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());
1097             }
1098
1099             return projects;
1100         } catch (IOException e) {
1101             throw new MetadataResolutionException(e.getMessage(), e);
1102         }
1103     }
1104
1105     @Override
1106     public Collection<String> getProjectVersions(RepositorySession session, String repoId, String namespace, String projectId)
1107             throws MetadataResolutionException {
1108         try {
1109             List<String> projectVersions;
1110             Path directory = getDirectory(repoId).resolve(namespace + "/" + projectId);
1111             if (!(Files.exists(directory) && Files.isDirectory(directory))) {
1112                 return Collections.emptyList();
1113             }
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());
1119             }
1120             return projectVersions;
1121         } catch (IOException e) {
1122             throw new MetadataResolutionException(e.getMessage(), e);
1123         }
1124     }
1125
1126     @Override
1127     public void removeProject(RepositorySession session, String repositoryId, String namespace, String projectId)
1128             throws MetadataRepositoryException {
1129         try {
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);
1134         }
1135     }
1136
1137     @Override
1138     public void removeProjectVersion(RepositorySession session, String repoId, String namespace, String projectId, String projectVersion)
1139             throws MetadataRepositoryException {
1140         try {
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);
1145         }
1146
1147     }
1148
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);
1154         }
1155     }
1156
1157     private static class ArtifactComparator
1158             implements Comparator<ArtifactMetadata> {
1159         @Override
1160         public int compare(ArtifactMetadata artifact1, ArtifactMetadata artifact2) {
1161             if (artifact1.getWhenGathered() == artifact2.getWhenGathered()) {
1162                 return 0;
1163             }
1164             if (artifact1.getWhenGathered() == null) {
1165                 return 1;
1166             }
1167             if (artifact2.getWhenGathered() == null) {
1168                 return -1;
1169             }
1170             return artifact1.getWhenGathered().compareTo(artifact2.getWhenGathered());
1171         }
1172     }
1173
1174     @Override
1175     public List<ArtifactMetadata> getArtifacts(RepositorySession session, String repoId)
1176             throws MetadataRepositoryException {
1177         try {
1178             List<ArtifactMetadata> artifacts = new ArrayList<>();
1179             for (String ns : getRootNamespaces(session, repoId)) {
1180                 getArtifacts(session, artifacts, repoId, ns);
1181             }
1182             return artifacts;
1183         } catch (MetadataResolutionException e) {
1184             throw new MetadataRepositoryException(e.getMessage(), e);
1185         }
1186     }
1187
1188     private class ArtifactCoordinates {
1189         final String namespace;
1190         final String project;
1191         final String version;
1192
1193         public ArtifactCoordinates(String namespace, String project, String version) {
1194             this.namespace = namespace;
1195             this.project = project;
1196             this.version = version;
1197         }
1198
1199         public String getNamespace( )
1200         {
1201             return namespace;
1202         }
1203
1204         public String getProject( )
1205         {
1206             return project;
1207         }
1208
1209         public String getVersion( )
1210         {
1211             return version;
1212         }
1213     }
1214
1215     @Override
1216     public Stream<ArtifactMetadata> getArtifactStream( @Nonnull final RepositorySession session, @Nonnull final String repositoryId,
1217                                                        @Nonnull QueryParameter queryParameter ) throws MetadataResolutionException
1218     {
1219
1220         return getAllNamespacesStream( session, repositoryId ).filter( Objects::nonNull ).flatMap( ns ->
1221             {
1222                 try
1223                 {
1224                     return getProjects( session, repositoryId, ns ).stream( ).map( proj ->
1225                         new ArtifactCoordinates( ns, proj, null ) );
1226                 }
1227                 catch ( MetadataResolutionException e )
1228                 {
1229                     return null;
1230                 }
1231             }
1232         ).filter( Objects::nonNull ).flatMap( artifactCoordinates ->
1233             {
1234                 try
1235                 {
1236                     return getProjectVersions( session, repositoryId, artifactCoordinates.getNamespace( ), artifactCoordinates.getProject( ) )
1237                         .stream( ).map(version -> new ArtifactCoordinates( artifactCoordinates.getNamespace(), artifactCoordinates.getProject(), version ));
1238                 }
1239                 catch ( MetadataResolutionException e )
1240                 {
1241                     return null;
1242                 }
1243             }
1244         ).filter( Objects::nonNull ).flatMap( ac ->
1245             {
1246                 try
1247                 {
1248                     return getArtifactStream( session, repositoryId, ac.getNamespace(), ac.getProject(), ac.getVersion() );
1249                 }
1250                 catch ( MetadataResolutionException e )
1251                 {
1252                     return null;
1253                 }
1254             }
1255             ).filter( Objects::nonNull );
1256     }
1257
1258     @Override
1259     public Stream<ArtifactMetadata> getArtifactStream( final RepositorySession session, final String repoId,
1260                                                        final String namespace, final String projectId,
1261                                                        final String projectVersion ) throws MetadataResolutionException
1262     {
1263         return getArtifacts( session, repoId, namespace, projectId, projectVersion ).stream( );
1264     }
1265
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);
1270         }
1271
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));
1275             }
1276         }
1277     }
1278
1279     @Override
1280     public List<ArtifactMetadata> searchArtifacts(RepositorySession session, String repositoryId, String text, boolean exact) {
1281         throw new UnsupportedOperationException("searchArtifacts not yet implemented in File backend");
1282     }
1283
1284     @Override
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");
1287     }
1288 }