This project has retired. For details please refer to its Attic page.
FileMetadataRepository xref
View Javadoc
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.configuration.ArchivaConfiguration;
23  import org.apache.archiva.configuration.ManagedRepositoryConfiguration;
24  import org.apache.archiva.metadata.model.ArtifactMetadata;
25  import org.apache.archiva.metadata.model.CiManagement;
26  import org.apache.archiva.metadata.model.Dependency;
27  import org.apache.archiva.metadata.model.IssueManagement;
28  import org.apache.archiva.metadata.model.License;
29  import org.apache.archiva.metadata.model.MailingList;
30  import org.apache.archiva.metadata.model.MetadataFacet;
31  import org.apache.archiva.metadata.model.MetadataFacetFactory;
32  import org.apache.archiva.metadata.model.Organization;
33  import org.apache.archiva.metadata.model.ProjectMetadata;
34  import org.apache.archiva.metadata.model.ProjectVersionMetadata;
35  import org.apache.archiva.metadata.model.ProjectVersionReference;
36  import org.apache.archiva.metadata.model.Scm;
37  import org.apache.archiva.metadata.repository.MetadataRepository;
38  import org.apache.archiva.metadata.repository.MetadataRepositoryException;
39  import org.apache.archiva.metadata.repository.MetadataResolutionException;
40  import org.apache.commons.io.FileUtils;
41  import org.slf4j.Logger;
42  import org.slf4j.LoggerFactory;
43  
44  import java.io.File;
45  import java.io.FileNotFoundException;
46  import java.io.IOException;
47  import java.io.InputStream;
48  import java.io.OutputStream;
49  import java.nio.file.Files;
50  import java.nio.file.NoSuchFileException;
51  import java.util.ArrayList;
52  import java.util.Arrays;
53  import java.util.Collection;
54  import java.util.Collections;
55  import java.util.Comparator;
56  import java.util.Date;
57  import java.util.HashMap;
58  import java.util.HashSet;
59  import java.util.LinkedHashSet;
60  import java.util.List;
61  import java.util.Map;
62  import java.util.Properties;
63  import java.util.Set;
64  import java.util.StringTokenizer;
65  
66  public class FileMetadataRepository
67      implements MetadataRepository
68  {
69      private final Map<String, MetadataFacetFactory> metadataFacetFactories;
70  
71      private final ArchivaConfiguration configuration;
72  
73      private Logger log = LoggerFactory.getLogger( FileMetadataRepository.class );
74  
75      private static final String PROJECT_METADATA_KEY = "project-metadata";
76  
77      private static final String PROJECT_VERSION_METADATA_KEY = "version-metadata";
78  
79      private static final String NAMESPACE_METADATA_KEY = "namespace-metadata";
80  
81      private static final String METADATA_KEY = "metadata";
82  
83      public FileMetadataRepository( Map<String, MetadataFacetFactory> metadataFacetFactories,
84                                     ArchivaConfiguration configuration )
85      {
86          this.metadataFacetFactories = metadataFacetFactories;
87          this.configuration = configuration;
88      }
89  
90      private File getBaseDirectory( String repoId )
91          throws IOException
92      {
93          // TODO: should be configurable, like the index
94          ManagedRepositoryConfiguration managedRepositoryConfiguration =
95              configuration.getConfiguration().getManagedRepositoriesAsMap().get( repoId );
96          if ( managedRepositoryConfiguration == null )
97          {
98              return Files.createTempDirectory( repoId ).toFile();
99          }
100         String basedir = managedRepositoryConfiguration.getLocation();
101         return new File( basedir, ".archiva" );
102     }
103 
104     private File getDirectory( String repoId )
105         throws IOException
106     {
107         return new File( getBaseDirectory( repoId ), "content" );
108     }
109 
110     @Override
111     public void updateProject( String repoId, ProjectMetadata project )
112     {
113         updateProject( repoId, project.getNamespace(), project.getId() );
114     }
115 
116     private void updateProject( String repoId, String namespace, String id )
117     {
118         // TODO: this is a more braindead implementation than we would normally expect, for prototyping purposes
119         updateNamespace( repoId, namespace );
120 
121         try
122         {
123             File namespaceDirectory = new File( getDirectory( repoId ), namespace );
124             Properties properties = new Properties();
125             properties.setProperty( "namespace", namespace );
126             properties.setProperty( "id", id );
127             writeProperties( properties, new File( namespaceDirectory, id ), PROJECT_METADATA_KEY );
128         }
129         catch ( IOException e )
130         {
131             // TODO!
132             log.error( e.getMessage(), e );
133         }
134     }
135 
136     @Override
137     public void updateProjectVersion( String repoId, String namespace, String projectId,
138                                       ProjectVersionMetadata versionMetadata )
139     {
140 
141         try
142         {
143             updateProject( repoId, namespace, projectId );
144 
145             File directory =
146                 new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + versionMetadata.getId() );
147 
148             Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
149             // remove properties that are not references or artifacts
150             for ( Object key : new ArrayList( properties.keySet() ) )
151             {
152                 String name = (String) key;
153                 if ( !name.contains( ":" ) && !name.equals( "facetIds" ) )
154                 {
155                     properties.remove( name );
156                 }
157 
158                 // clear the facet contents so old properties are no longer written
159                 clearMetadataFacetProperties( versionMetadata.getFacetList(), properties, "" );
160             }
161             properties.setProperty( "id", versionMetadata.getId() );
162             setProperty( properties, "name", versionMetadata.getName() );
163             setProperty( properties, "description", versionMetadata.getDescription() );
164             setProperty( properties, "url", versionMetadata.getUrl() );
165             setProperty( properties, "incomplete", String.valueOf( versionMetadata.isIncomplete() ) );
166             if ( versionMetadata.getScm() != null )
167             {
168                 setProperty( properties, "scm.connection", versionMetadata.getScm().getConnection() );
169                 setProperty( properties, "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
170                 setProperty( properties, "scm.url", versionMetadata.getScm().getUrl() );
171             }
172             if ( versionMetadata.getCiManagement() != null )
173             {
174                 setProperty( properties, "ci.system", versionMetadata.getCiManagement().getSystem() );
175                 setProperty( properties, "ci.url", versionMetadata.getCiManagement().getUrl() );
176             }
177             if ( versionMetadata.getIssueManagement() != null )
178             {
179                 setProperty( properties, "issue.system", versionMetadata.getIssueManagement().getSystem() );
180                 setProperty( properties, "issue.url", versionMetadata.getIssueManagement().getUrl() );
181             }
182             if ( versionMetadata.getOrganization() != null )
183             {
184                 setProperty( properties, "org.name", versionMetadata.getOrganization().getName() );
185                 setProperty( properties, "org.url", versionMetadata.getOrganization().getUrl() );
186             }
187             int i = 0;
188             for ( License license : versionMetadata.getLicenses() )
189             {
190                 setProperty( properties, "license." + i + ".name", license.getName() );
191                 setProperty( properties, "license." + i + ".url", license.getUrl() );
192                 i++;
193             }
194             i = 0;
195             for ( MailingList mailingList : versionMetadata.getMailingLists() )
196             {
197                 setProperty( properties, "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
198                 setProperty( properties, "mailingList." + i + ".name", mailingList.getName() );
199                 setProperty( properties, "mailingList." + i + ".post", mailingList.getPostAddress() );
200                 setProperty( properties, "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
201                 setProperty( properties, "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
202                 setProperty( properties, "mailingList." + i + ".otherArchives",
203                              join( mailingList.getOtherArchives() ) );
204                 i++;
205             }
206             i = 0;
207             ProjectVersionReference reference = new ProjectVersionReference();
208             reference.setNamespace( namespace );
209             reference.setProjectId( projectId );
210             reference.setProjectVersion( versionMetadata.getId() );
211             reference.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
212             for ( Dependency dependency : versionMetadata.getDependencies() )
213             {
214                 setProperty( properties, "dependency." + i + ".classifier", dependency.getClassifier() );
215                 setProperty( properties, "dependency." + i + ".scope", dependency.getScope() );
216                 setProperty( properties, "dependency." + i + ".systemPath", dependency.getSystemPath() );
217                 setProperty( properties, "dependency." + i + ".artifactId", dependency.getArtifactId() );
218                 setProperty( properties, "dependency." + i + ".groupId", dependency.getGroupId() );
219                 setProperty( properties, "dependency." + i + ".version", dependency.getVersion() );
220                 setProperty( properties, "dependency." + i + ".type", dependency.getType() );
221                 setProperty( properties, "dependency." + i + ".optional", String.valueOf( dependency.isOptional() ) );
222 
223                 updateProjectReference( repoId, dependency.getGroupId(), dependency.getArtifactId(),
224                                         dependency.getVersion(), reference );
225 
226                 i++;
227             }
228             Set<String> facetIds = new LinkedHashSet<String>( versionMetadata.getFacetIds() );
229             facetIds.addAll( Arrays.asList( properties.getProperty( "facetIds", "" ).split( "," ) ) );
230             properties.setProperty( "facetIds", join( facetIds ) );
231 
232             updateProjectVersionFacets( versionMetadata, properties );
233 
234             writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
235         }
236         catch ( IOException e )
237         {
238             // TODO
239             log.error( e.getMessage(), e );
240         }
241     }
242 
243     private void updateProjectVersionFacets( ProjectVersionMetadata versionMetadata, Properties properties )
244     {
245         for ( MetadataFacet facet : versionMetadata.getFacetList() )
246         {
247             for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
248             {
249                 properties.setProperty( facet.getFacetId() + ":" + entry.getKey(), entry.getValue() );
250             }
251         }
252     }
253 
254     private static void clearMetadataFacetProperties( Collection<MetadataFacet> facetList, Properties properties,
255                                                       String prefix )
256     {
257         List<Object> propsToRemove = new ArrayList<>();
258         for ( MetadataFacet facet : facetList )
259         {
260             for ( Object key : new ArrayList( properties.keySet() ) )
261             {
262                 String keyString = (String) key;
263                 if ( keyString.startsWith( prefix + facet.getFacetId() + ":" ) )
264                 {
265                     propsToRemove.add( key );
266                 }
267             }
268         }
269 
270         for ( Object key : propsToRemove )
271         {
272             properties.remove( key );
273         }
274     }
275 
276     private void updateProjectReference( String repoId, String namespace, String projectId, String projectVersion,
277                                          ProjectVersionReference reference )
278     {
279         try
280         {
281             File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
282 
283             Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
284             int i = Integer.parseInt( properties.getProperty( "ref:lastReferenceNum", "-1" ) ) + 1;
285             setProperty( properties, "ref:lastReferenceNum", Integer.toString( i ) );
286             setProperty( properties, "ref:reference." + i + ".namespace", reference.getNamespace() );
287             setProperty( properties, "ref:reference." + i + ".projectId", reference.getProjectId() );
288             setProperty( properties, "ref:reference." + i + ".projectVersion", reference.getProjectVersion() );
289             setProperty( properties, "ref:reference." + i + ".referenceType", reference.getReferenceType().toString() );
290 
291             writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
292         }
293         catch ( IOException e )
294         {
295             // TODO
296             log.error( e.getMessage(), e );
297         }
298     }
299 
300     @Override
301     public void updateNamespace( String repoId, String namespace )
302     {
303         try
304         {
305             File namespaceDirectory = new File( getDirectory( repoId ), namespace );
306             Properties properties = new Properties();
307             properties.setProperty( "namespace", namespace );
308             writeProperties( properties, namespaceDirectory, NAMESPACE_METADATA_KEY );
309 
310         }
311         catch ( IOException e )
312         {
313             // TODO!
314             log.error( e.getMessage(), e );
315         }
316     }
317 
318     @Override
319     public List<String> getMetadataFacets( String repoId, String facetId )
320         throws MetadataRepositoryException
321     {
322         try
323         {
324             File directory = getMetadataDirectory( repoId, facetId );
325             List<String> facets = new ArrayList<>();
326             recurse( facets, "", directory );
327             return facets;
328         }
329         catch ( IOException e )
330         {
331             throw new MetadataRepositoryException( e.getMessage(), e );
332         }
333     }
334 
335     @Override
336     public boolean hasMetadataFacet( String repositoryId, String facetId )
337         throws MetadataRepositoryException
338     {
339         // TODO could be improved a bit
340         return !getMetadataFacets( repositoryId, facetId ).isEmpty();
341     }
342 
343     private void recurse( List<String> facets, String prefix, File directory )
344     {
345         File[] list = directory.listFiles();
346         if ( list != null )
347         {
348             for ( File dir : list )
349             {
350                 if ( dir.isDirectory() )
351                 {
352                     recurse( facets, prefix + "/" + dir.getName(), dir );
353                 }
354                 else if ( dir.getName().equals( METADATA_KEY + ".properties" ) )
355                 {
356                     facets.add( prefix.substring( 1 ) );
357                 }
358             }
359         }
360     }
361 
362     @Override
363     public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
364     {
365         Properties properties;
366         try
367         {
368             properties =
369                 readProperties( new File( getMetadataDirectory( repositoryId, facetId ), name ), METADATA_KEY );
370         }
371         catch ( FileNotFoundException e )
372         {
373             return null;
374         }
375         catch ( IOException e )
376         {
377             // TODO
378             log.error( e.getMessage(), e );
379             return null;
380         }
381         MetadataFacet metadataFacet = null;
382         MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
383         if ( metadataFacetFactory != null )
384         {
385             metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
386             Map<String, String> map = new HashMap<>();
387             for ( Object key : new ArrayList( properties.keySet() ) )
388             {
389                 String property = (String) key;
390                 map.put( property, properties.getProperty( property ) );
391             }
392             metadataFacet.fromProperties( map );
393         }
394         return metadataFacet;
395     }
396 
397     @Override
398     public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
399     {
400         Properties properties = new Properties();
401         properties.putAll( metadataFacet.toProperties() );
402 
403         try
404         {
405             File directory =
406                 new File( getMetadataDirectory( repositoryId, metadataFacet.getFacetId() ), metadataFacet.getName() );
407             writeProperties( properties, directory, METADATA_KEY );
408         }
409         catch ( IOException e )
410         {
411             // TODO!
412             log.error( e.getMessage(), e );
413         }
414     }
415 
416     @Override
417     public void removeMetadataFacets( String repositoryId, String facetId )
418         throws MetadataRepositoryException
419     {
420         try
421         {
422             File dir = getMetadataDirectory( repositoryId, facetId );
423             FileUtils.deleteDirectory( dir );
424         }
425         catch ( IOException e )
426         {
427             throw new MetadataRepositoryException( e.getMessage(), e );
428         }
429     }
430 
431     @Override
432     public void removeMetadataFacet( String repoId, String facetId, String name )
433         throws MetadataRepositoryException
434     {
435         try
436         {
437             File dir = new File( getMetadataDirectory( repoId, facetId ), name );
438             FileUtils.deleteDirectory( dir );
439         }
440         catch ( IOException e )
441         {
442             throw new MetadataRepositoryException( e.getMessage(), e );
443         }
444     }
445 
446     @Override
447     public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
448         throws MetadataRepositoryException
449     {
450         try
451         {
452             // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
453             //  of this information (eg. in Lucene, as before)
454 
455             List<ArtifactMetadata> artifacts = new ArrayList<>();
456             for ( String ns : getRootNamespaces( repoId ) )
457             {
458                 getArtifactsByDateRange( artifacts, repoId, ns, startTime, endTime );
459             }
460             Collections.sort( artifacts, new ArtifactComparator() );
461             return artifacts;
462         }
463         catch ( MetadataResolutionException e )
464         {
465             throw new MetadataRepositoryException( e.getMessage(), e );
466         }
467     }
468 
469     private void getArtifactsByDateRange( List<ArtifactMetadata> artifacts, String repoId, String ns, Date startTime,
470                                           Date endTime )
471         throws MetadataRepositoryException
472     {
473         try
474         {
475             for ( String namespace : getNamespaces( repoId, ns ) )
476             {
477                 getArtifactsByDateRange( artifacts, repoId, ns + "." + namespace, startTime, endTime );
478             }
479 
480             for ( String project : getProjects( repoId, ns ) )
481             {
482                 for ( String version : getProjectVersions( repoId, ns, project ) )
483                 {
484                     for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) )
485                     {
486                         if ( startTime == null || startTime.before( artifact.getWhenGathered() ) )
487                         {
488                             if ( endTime == null || endTime.after( artifact.getWhenGathered() ) )
489                             {
490                                 artifacts.add( artifact );
491                             }
492                         }
493                     }
494                 }
495             }
496         }
497         catch ( MetadataResolutionException e )
498         {
499             throw new MetadataRepositoryException( e.getMessage(), e );
500         }
501     }
502 
503     @Override
504     public Collection<ArtifactMetadata> getArtifacts( String repoId, String namespace, String projectId,
505                                                       String projectVersion )
506         throws MetadataResolutionException
507     {
508         try
509         {
510             Map<String, ArtifactMetadata> artifacts = new HashMap<>();
511 
512             File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
513 
514             Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
515 
516             for ( Map.Entry entry : properties.entrySet() )
517             {
518                 String name = (String) entry.getKey();
519                 StringTokenizer tok = new StringTokenizer( name, ":" );
520                 if ( tok.hasMoreTokens() && "artifact".equals( tok.nextToken() ) )
521                 {
522                     String field = tok.nextToken();
523                     String id = tok.nextToken();
524 
525                     ArtifactMetadata artifact = artifacts.get( id );
526                     if ( artifact == null )
527                     {
528                         artifact = new ArtifactMetadata();
529                         artifact.setRepositoryId( repoId );
530                         artifact.setNamespace( namespace );
531                         artifact.setProject( projectId );
532                         artifact.setProjectVersion( projectVersion );
533                         artifact.setVersion( projectVersion );
534                         artifact.setId( id );
535                         artifacts.put( id, artifact );
536                     }
537 
538                     String value = (String) entry.getValue();
539                     if ( "updated".equals( field ) )
540                     {
541                         artifact.setFileLastModified( Long.parseLong( value ) );
542                     }
543                     else if ( "size".equals( field ) )
544                     {
545                         artifact.setSize( Long.valueOf( value ) );
546                     }
547                     else if ( "whenGathered".equals( field ) )
548                     {
549                         artifact.setWhenGathered( new Date( Long.parseLong( value ) ) );
550                     }
551                     else if ( "version".equals( field ) )
552                     {
553                         artifact.setVersion( value );
554                     }
555                     else if ( "md5".equals( field ) )
556                     {
557                         artifact.setMd5( value );
558                     }
559                     else if ( "sha1".equals( field ) )
560                     {
561                         artifact.setSha1( value );
562                     }
563                     else if ( "facetIds".equals( field ) )
564                     {
565                         if ( value.length() > 0 )
566                         {
567                             String propertyPrefix = "artifact:facet:" + id + ":";
568                             for ( String facetId : value.split( "," ) )
569                             {
570                                 MetadataFacetFactory factory = metadataFacetFactories.get( facetId );
571                                 if ( factory == null )
572                                 {
573                                     log.error( "Attempted to load unknown artifact metadata facet: " + facetId );
574                                 }
575                                 else
576                                 {
577                                     MetadataFacet facet = factory.createMetadataFacet();
578                                     String prefix = propertyPrefix + facet.getFacetId();
579                                     Map<String, String> map = new HashMap<>();
580                                     for ( Object key : new ArrayList( properties.keySet() ) )
581                                     {
582                                         String property = (String) key;
583                                         if ( property.startsWith( prefix ) )
584                                         {
585                                             map.put( property.substring( prefix.length() + 1 ),
586                                                      properties.getProperty( property ) );
587                                         }
588                                     }
589                                     facet.fromProperties( map );
590                                     artifact.addFacet( facet );
591                                 }
592                             }
593                         }
594 
595                         updateArtifactFacets( artifact, properties );
596                     }
597                 }
598             }
599             return artifacts.values();
600         }
601         catch ( IOException e )
602         {
603             throw new MetadataResolutionException( e.getMessage(), e );
604         }
605     }
606 
607     @Override
608     public void save()
609     {
610         // it's all instantly persisted
611     }
612 
613     @Override
614     public void close()
615     {
616         // nothing additional to close
617     }
618 
619     @Override
620     public void revert()
621     {
622         log.warn( "Attempted to revert a session, but the file-based repository storage doesn't support it" );
623     }
624 
625     @Override
626     public boolean canObtainAccess( Class<?> aClass )
627     {
628         return false;
629     }
630 
631     @Override
632     public <T> T obtainAccess( Class<T> aClass )
633     {
634         throw new IllegalArgumentException(
635             "Access using " + aClass + " is not supported on the file metadata storage" );
636     }
637 
638     private void updateArtifactFacets( ArtifactMetadata artifact, Properties properties )
639     {
640         String propertyPrefix = "artifact:facet:" + artifact.getId() + ":";
641         for ( MetadataFacet facet : artifact.getFacetList() )
642         {
643             for ( Map.Entry<String, String> e : facet.toProperties().entrySet() )
644             {
645                 String key = propertyPrefix + facet.getFacetId() + ":" + e.getKey();
646                 properties.setProperty( key, e.getValue() );
647             }
648         }
649     }
650 
651     @Override
652     public Collection<String> getRepositories()
653     {
654         List<String> repositories = new ArrayList<>();
655         for ( ManagedRepositoryConfiguration managedRepositoryConfiguration : configuration.getConfiguration().getManagedRepositories() )
656         {
657             repositories.add( managedRepositoryConfiguration.getId() );
658         }
659         return repositories;
660     }
661 
662     @Override
663     public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
664         throws MetadataRepositoryException
665     {
666         try
667         {
668             // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
669             //  of this information (eg. in Lucene, as before)
670             // alternatively, we could build a referential tree in the content repository, however it would need some levels
671             // of depth to avoid being too broad to be useful (eg. /repository/checksums/a/ab/abcdef1234567)
672 
673             List<ArtifactMetadata> artifacts = new ArrayList<>();
674             for ( String ns : getRootNamespaces( repositoryId ) )
675             {
676                 getArtifactsByChecksum( artifacts, repositoryId, ns, checksum );
677             }
678             return artifacts;
679         }
680         catch ( MetadataResolutionException e )
681         {
682             throw new MetadataRepositoryException( e.getMessage(), e );
683         }
684     }
685 
686     @Override
687     public void removeNamespace( String repositoryId, String project )
688         throws MetadataRepositoryException
689     {
690         try
691         {
692             File namespaceDirectory = new File( getDirectory( repositoryId ), project );
693             FileUtils.deleteDirectory( namespaceDirectory );
694             //Properties properties = new Properties();
695             //properties.setProperty( "namespace", namespace );
696             //writeProperties( properties, namespaceDirectory, NAMESPACE_METADATA_KEY );
697 
698         }
699         catch ( IOException e )
700         {
701             throw new MetadataRepositoryException( e.getMessage(), e );
702         }
703     }
704 
705     @Override
706     public void removeArtifact( ArtifactMetadata artifactMetadata, String baseVersion )
707         throws MetadataRepositoryException
708     {
709 
710         try
711         {
712             File directory = new File( getDirectory( artifactMetadata.getRepositoryId() ),
713                                        artifactMetadata.getNamespace() + "/" + artifactMetadata.getProject() + "/"
714                                            + baseVersion );
715 
716             Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
717 
718             String id = artifactMetadata.getId();
719 
720             properties.remove( "artifact:updated:" + id );
721             properties.remove( "artifact:whenGathered:" + id );
722             properties.remove( "artifact:size:" + id );
723             properties.remove( "artifact:md5:" + id );
724             properties.remove( "artifact:sha1:" + id );
725             properties.remove( "artifact:version:" + id );
726             properties.remove( "artifact:facetIds:" + id );
727 
728             String prefix = "artifact:facet:" + id + ":";
729             for ( Object key : new ArrayList( properties.keySet() ) )
730             {
731                 String property = (String) key;
732                 if ( property.startsWith( prefix ) )
733                 {
734                     properties.remove( property );
735                 }
736             }
737 
738             writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
739         }
740         catch ( IOException e )
741         {
742             throw new MetadataRepositoryException( e.getMessage(), e );
743         }
744 
745     }
746 
747     @Override
748     public void removeArtifact( String repoId, String namespace, String project, String version, String id )
749         throws MetadataRepositoryException
750     {
751         try
752         {
753             File directory = new File( getDirectory( repoId ), namespace + "/" + project + "/" + version );
754 
755             Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
756 
757             properties.remove( "artifact:updated:" + id );
758             properties.remove( "artifact:whenGathered:" + id );
759             properties.remove( "artifact:size:" + id );
760             properties.remove( "artifact:md5:" + id );
761             properties.remove( "artifact:sha1:" + id );
762             properties.remove( "artifact:version:" + id );
763             properties.remove( "artifact:facetIds:" + id );
764 
765             String prefix = "artifact:facet:" + id + ":";
766             for ( Object key : new ArrayList( properties.keySet() ) )
767             {
768                 String property = (String) key;
769                 if ( property.startsWith( prefix ) )
770                 {
771                     properties.remove( property );
772                 }
773             }
774 
775             FileUtils.deleteDirectory( directory );
776             //writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
777         }
778         catch ( IOException e )
779         {
780             throw new MetadataRepositoryException( e.getMessage(), e );
781         }
782     }
783 
784     /**
785      * FIXME implements this !!!!
786      *
787      * @param repositoryId
788      * @param namespace
789      * @param project
790      * @param projectVersion
791      * @param metadataFacet  will remove artifacts which have this {@link MetadataFacet} using equals
792      * @throws MetadataRepositoryException
793      */
794     @Override
795     public void removeArtifact( String repositoryId, String namespace, String project, String projectVersion,
796                                 MetadataFacet metadataFacet )
797         throws MetadataRepositoryException
798     {
799         throw new UnsupportedOperationException( "not implemented" );
800     }
801 
802     @Override
803     public void removeRepository( String repoId )
804         throws MetadataRepositoryException
805     {
806         try
807         {
808             File dir = getDirectory( repoId );
809             FileUtils.deleteDirectory( dir );
810         }
811         catch ( IOException e )
812         {
813             throw new MetadataRepositoryException( e.getMessage(), e );
814         }
815     }
816 
817     private void getArtifactsByChecksum( List<ArtifactMetadata> artifacts, String repositoryId, String ns,
818                                          String checksum )
819         throws MetadataRepositoryException
820     {
821         try
822         {
823             for ( String namespace : getNamespaces( repositoryId, ns ) )
824             {
825                 getArtifactsByChecksum( artifacts, repositoryId, ns + "." + namespace, checksum );
826             }
827 
828             for ( String project : getProjects( repositoryId, ns ) )
829             {
830                 for ( String version : getProjectVersions( repositoryId, ns, project ) )
831                 {
832                     for ( ArtifactMetadata artifact : getArtifacts( repositoryId, ns, project, version ) )
833                     {
834                         if ( checksum.equals( artifact.getMd5() ) || checksum.equals( artifact.getSha1() ) )
835                         {
836                             artifacts.add( artifact );
837                         }
838                     }
839                 }
840             }
841         }
842         catch ( MetadataResolutionException e )
843         {
844             throw new MetadataRepositoryException( e.getMessage(), e );
845         }
846     }
847 
848     @Override
849     public List<ArtifactMetadata> getArtifactsByProjectVersionMetadata( String key, String value, String repositoryId )
850         throws MetadataRepositoryException
851     {
852         throw new UnsupportedOperationException( "not yet implemented in File backend" );
853     }
854 
855     @Override
856     public List<ArtifactMetadata> getArtifactsByMetadata( String key, String value, String repositoryId )
857         throws MetadataRepositoryException
858     {
859         throw new UnsupportedOperationException( "not yet implemented in File backend" );
860     }
861 
862     @Override
863     public List<ArtifactMetadata> getArtifactsByProperty( String key, String value, String repositoryId )
864         throws MetadataRepositoryException
865     {
866         throw new UnsupportedOperationException( "getArtifactsByProperty not yet implemented in File backend" );
867     }
868 
869     private File getMetadataDirectory( String repoId, String facetId )
870         throws IOException
871     {
872         return new File( getBaseDirectory( repoId ), "facets/" + facetId );
873     }
874 
875     private String join( Collection<String> ids )
876     {
877         if ( ids != null && !ids.isEmpty() )
878         {
879             StringBuilder s = new StringBuilder();
880             for ( String id : ids )
881             {
882                 s.append( id );
883                 s.append( "," );
884             }
885             return s.substring( 0, s.length() - 1 );
886         }
887         return "";
888     }
889 
890     private void setProperty( Properties properties, String name, String value )
891     {
892         if ( value != null )
893         {
894             properties.setProperty( name, value );
895         }
896     }
897 
898     @Override
899     public void updateArtifact( String repoId, String namespace, String projectId, String projectVersion,
900                                 ArtifactMetadata artifact )
901     {
902         try
903         {
904             ProjectVersionMetadata metadata = new ProjectVersionMetadata();
905             metadata.setId( projectVersion );
906             updateProjectVersion( repoId, namespace, projectId, metadata );
907 
908             File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
909 
910             Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
911 
912             clearMetadataFacetProperties( artifact.getFacetList(), properties,
913                                           "artifact:facet:" + artifact.getId() + ":" );
914 
915             String id = artifact.getId();
916             properties.setProperty( "artifact:updated:" + id,
917                                     Long.toString( artifact.getFileLastModified().getTime() ) );
918             properties.setProperty( "artifact:whenGathered:" + id,
919                                     Long.toString( artifact.getWhenGathered().getTime() ) );
920             properties.setProperty( "artifact:size:" + id, Long.toString( artifact.getSize() ) );
921             if ( artifact.getMd5() != null )
922             {
923                 properties.setProperty( "artifact:md5:" + id, artifact.getMd5() );
924             }
925             if ( artifact.getSha1() != null )
926             {
927                 properties.setProperty( "artifact:sha1:" + id, artifact.getSha1() );
928             }
929             properties.setProperty( "artifact:version:" + id, artifact.getVersion() );
930 
931             Set<String> facetIds = new LinkedHashSet<String>( artifact.getFacetIds() );
932             String property = "artifact:facetIds:" + id;
933             facetIds.addAll( Arrays.asList( properties.getProperty( property, "" ).split( "," ) ) );
934             properties.setProperty( property, join( facetIds ) );
935 
936             updateArtifactFacets( artifact, properties );
937 
938             writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
939         }
940         catch ( IOException e )
941         {
942             // TODO
943             log.error( e.getMessage(), e );
944         }
945     }
946 
947     private Properties readOrCreateProperties( File directory, String propertiesKey )
948     {
949         try
950         {
951             return readProperties( directory, propertiesKey );
952         }
953         catch ( FileNotFoundException | NoSuchFileException e )
954         {
955             // ignore and return new properties
956         }
957         catch ( IOException e )
958         {
959             // TODO
960             log.error( e.getMessage(), e );
961         }
962         return new Properties();
963     }
964 
965     private Properties readProperties( File directory, String propertiesKey )
966         throws IOException
967     {
968         Properties properties = new Properties();
969         try (InputStream in = Files.newInputStream( new File( directory, propertiesKey + ".properties" ).toPath() ))
970         {
971 
972             properties.load( in );
973         }
974         return properties;
975     }
976 
977     @Override
978     public ProjectMetadata getProject( String repoId, String namespace, String projectId )
979         throws MetadataResolutionException
980     {
981         try
982         {
983             File directory = new File( getDirectory( repoId ), namespace + "/" + projectId );
984 
985             Properties properties = readOrCreateProperties( directory, PROJECT_METADATA_KEY );
986 
987             ProjectMetadata project = null;
988 
989             String id = properties.getProperty( "id" );
990             if ( id != null )
991             {
992                 project = new ProjectMetadata();
993                 project.setNamespace( properties.getProperty( "namespace" ) );
994                 project.setId( id );
995             }
996 
997             return project;
998         }
999         catch ( IOException e )
1000         {
1001             throw new MetadataResolutionException( e.getMessage(), e );
1002         }
1003     }
1004 
1005     @Override
1006     public ProjectVersionMetadata getProjectVersion( String repoId, String namespace, String projectId,
1007                                                      String projectVersion )
1008         throws MetadataResolutionException
1009     {
1010         try
1011         {
1012             File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
1013 
1014             Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
1015             String id = properties.getProperty( "id" );
1016             ProjectVersionMetadata versionMetadata = null;
1017             if ( id != null )
1018             {
1019                 versionMetadata = new ProjectVersionMetadata();
1020                 versionMetadata.setId( id );
1021                 versionMetadata.setName( properties.getProperty( "name" ) );
1022                 versionMetadata.setDescription( properties.getProperty( "description" ) );
1023                 versionMetadata.setUrl( properties.getProperty( "url" ) );
1024                 versionMetadata.setIncomplete( Boolean.valueOf( properties.getProperty( "incomplete", "false" ) ) );
1025 
1026                 String scmConnection = properties.getProperty( "scm.connection" );
1027                 String scmDeveloperConnection = properties.getProperty( "scm.developerConnection" );
1028                 String scmUrl = properties.getProperty( "scm.url" );
1029                 if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
1030                 {
1031                     Scm scm = new Scm();
1032                     scm.setConnection( scmConnection );
1033                     scm.setDeveloperConnection( scmDeveloperConnection );
1034                     scm.setUrl( scmUrl );
1035                     versionMetadata.setScm( scm );
1036                 }
1037 
1038                 String ciSystem = properties.getProperty( "ci.system" );
1039                 String ciUrl = properties.getProperty( "ci.url" );
1040                 if ( ciSystem != null || ciUrl != null )
1041                 {
1042                     CiManagement ci = new CiManagement();
1043                     ci.setSystem( ciSystem );
1044                     ci.setUrl( ciUrl );
1045                     versionMetadata.setCiManagement( ci );
1046                 }
1047 
1048                 String issueSystem = properties.getProperty( "issue.system" );
1049                 String issueUrl = properties.getProperty( "issue.url" );
1050                 if ( issueSystem != null || issueUrl != null )
1051                 {
1052                     IssueManagement issueManagement = new IssueManagement();
1053                     issueManagement.setSystem( issueSystem );
1054                     issueManagement.setUrl( issueUrl );
1055                     versionMetadata.setIssueManagement( issueManagement );
1056                 }
1057 
1058                 String orgName = properties.getProperty( "org.name" );
1059                 String orgUrl = properties.getProperty( "org.url" );
1060                 if ( orgName != null || orgUrl != null )
1061                 {
1062                     Organization org = new Organization();
1063                     org.setName( orgName );
1064                     org.setUrl( orgUrl );
1065                     versionMetadata.setOrganization( org );
1066                 }
1067 
1068                 boolean done = false;
1069                 int i = 0;
1070                 while ( !done )
1071                 {
1072                     String licenseName = properties.getProperty( "license." + i + ".name" );
1073                     String licenseUrl = properties.getProperty( "license." + i + ".url" );
1074                     if ( licenseName != null || licenseUrl != null )
1075                     {
1076                         License license = new License();
1077                         license.setName( licenseName );
1078                         license.setUrl( licenseUrl );
1079                         versionMetadata.addLicense( license );
1080                     }
1081                     else
1082                     {
1083                         done = true;
1084                     }
1085                     i++;
1086                 }
1087 
1088                 done = false;
1089                 i = 0;
1090                 while ( !done )
1091                 {
1092                     String mailingListName = properties.getProperty( "mailingList." + i + ".name" );
1093                     if ( mailingListName != null )
1094                     {
1095                         MailingList mailingList = new MailingList();
1096                         mailingList.setName( mailingListName );
1097                         mailingList.setMainArchiveUrl( properties.getProperty( "mailingList." + i + ".archive" ) );
1098                         String p = properties.getProperty( "mailingList." + i + ".otherArchives" );
1099                         if ( p != null && p.length() > 0 )
1100                         {
1101                             mailingList.setOtherArchives( Arrays.asList( p.split( "," ) ) );
1102                         }
1103                         else
1104                         {
1105                             mailingList.setOtherArchives( Collections.<String>emptyList() );
1106                         }
1107                         mailingList.setPostAddress( properties.getProperty( "mailingList." + i + ".post" ) );
1108                         mailingList.setSubscribeAddress( properties.getProperty( "mailingList." + i + ".subscribe" ) );
1109                         mailingList.setUnsubscribeAddress(
1110                             properties.getProperty( "mailingList." + i + ".unsubscribe" ) );
1111                         versionMetadata.addMailingList( mailingList );
1112                     }
1113                     else
1114                     {
1115                         done = true;
1116                     }
1117                     i++;
1118                 }
1119 
1120                 done = false;
1121                 i = 0;
1122                 while ( !done )
1123                 {
1124                     String dependencyArtifactId = properties.getProperty( "dependency." + i + ".artifactId" );
1125                     if ( dependencyArtifactId != null )
1126                     {
1127                         Dependency dependency = new Dependency();
1128                         dependency.setArtifactId( dependencyArtifactId );
1129                         dependency.setGroupId( properties.getProperty( "dependency." + i + ".groupId" ) );
1130                         dependency.setClassifier( properties.getProperty( "dependency." + i + ".classifier" ) );
1131                         dependency.setOptional(
1132                             Boolean.valueOf( properties.getProperty( "dependency." + i + ".optional" ) ) );
1133                         dependency.setScope( properties.getProperty( "dependency." + i + ".scope" ) );
1134                         dependency.setSystemPath( properties.getProperty( "dependency." + i + ".systemPath" ) );
1135                         dependency.setType( properties.getProperty( "dependency." + i + ".type" ) );
1136                         dependency.setVersion( properties.getProperty( "dependency." + i + ".version" ) );
1137                         dependency.setOptional(
1138                             Boolean.valueOf( properties.getProperty( "dependency." + i + ".optional" ) ) );
1139                         versionMetadata.addDependency( dependency );
1140                     }
1141                     else
1142                     {
1143                         done = true;
1144                     }
1145                     i++;
1146                 }
1147 
1148                 String facetIds = properties.getProperty( "facetIds", "" );
1149                 if ( facetIds.length() > 0 )
1150                 {
1151                     for ( String facetId : facetIds.split( "," ) )
1152                     {
1153                         MetadataFacetFactory factory = metadataFacetFactories.get( facetId );
1154                         if ( factory == null )
1155                         {
1156                             log.error( "Attempted to load unknown project version metadata facet: {}", facetId );
1157                         }
1158                         else
1159                         {
1160                             MetadataFacet facet = factory.createMetadataFacet();
1161                             Map<String, String> map = new HashMap<>();
1162                             for ( Object key : new ArrayList( properties.keySet() ) )
1163                             {
1164                                 String property = (String) key;
1165                                 if ( property.startsWith( facet.getFacetId() ) )
1166                                 {
1167                                     map.put( property.substring( facet.getFacetId().length() + 1 ),
1168                                              properties.getProperty( property ) );
1169                                 }
1170                             }
1171                             facet.fromProperties( map );
1172                             versionMetadata.addFacet( facet );
1173                         }
1174                     }
1175                 }
1176 
1177                 updateProjectVersionFacets( versionMetadata, properties );
1178             }
1179             return versionMetadata;
1180         }
1181         catch ( IOException e )
1182         {
1183             throw new MetadataResolutionException( e.getMessage(), e );
1184         }
1185     }
1186 
1187     @Override
1188     public Collection<String> getArtifactVersions( String repoId, String namespace, String projectId,
1189                                                    String projectVersion )
1190         throws MetadataResolutionException
1191     {
1192         try
1193         {
1194             File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
1195 
1196             Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
1197 
1198             Set<String> versions = new HashSet<String>();
1199             for ( Map.Entry entry : properties.entrySet() )
1200             {
1201                 String name = (String) entry.getKey();
1202                 if ( name.startsWith( "artifact:version:" ) )
1203                 {
1204                     versions.add( (String) entry.getValue() );
1205                 }
1206             }
1207             return versions;
1208         }
1209         catch ( IOException e )
1210         {
1211             throw new MetadataResolutionException( e.getMessage(), e );
1212         }
1213     }
1214 
1215     @Override
1216     public Collection<ProjectVersionReference> getProjectReferences( String repoId, String namespace, String projectId,
1217                                                                      String projectVersion )
1218         throws MetadataResolutionException
1219     {
1220         try
1221         {
1222             File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
1223 
1224             Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
1225             int numberOfRefs = Integer.parseInt( properties.getProperty( "ref:lastReferenceNum", "-1" ) ) + 1;
1226 
1227             List<ProjectVersionReference> references = new ArrayList<>();
1228             for ( int i = 0; i < numberOfRefs; i++ )
1229             {
1230                 ProjectVersionReference reference = new ProjectVersionReference();
1231                 reference.setProjectId( properties.getProperty( "ref:reference." + i + ".projectId" ) );
1232                 reference.setNamespace( properties.getProperty( "ref:reference." + i + ".namespace" ) );
1233                 reference.setProjectVersion( properties.getProperty( "ref:reference." + i + ".projectVersion" ) );
1234                 reference.setReferenceType( ProjectVersionReference.ReferenceType.valueOf(
1235                     properties.getProperty( "ref:reference." + i + ".referenceType" ) ) );
1236                 references.add( reference );
1237             }
1238             return references;
1239         }
1240         catch ( IOException e )
1241         {
1242             throw new MetadataResolutionException( e.getMessage(), e );
1243         }
1244     }
1245 
1246     @Override
1247     public Collection<String> getRootNamespaces( String repoId )
1248         throws MetadataResolutionException
1249     {
1250         return getNamespaces( repoId, null );
1251     }
1252 
1253     @Override
1254     public Collection<String> getNamespaces( String repoId, String baseNamespace )
1255         throws MetadataResolutionException
1256     {
1257         try
1258         {
1259             List<String> allNamespaces = new ArrayList<>();
1260             File directory = getDirectory( repoId );
1261             File[] files = directory.listFiles();
1262             if ( files != null )
1263             {
1264                 for ( File namespace : files )
1265                 {
1266                     if ( new File( namespace, NAMESPACE_METADATA_KEY + ".properties" ).exists() )
1267                     {
1268                         allNamespaces.add( namespace.getName() );
1269                     }
1270                 }
1271             }
1272 
1273             Set<String> namespaces = new LinkedHashSet<>();
1274             int fromIndex = baseNamespace != null ? baseNamespace.length() + 1 : 0;
1275             for ( String namespace : allNamespaces )
1276             {
1277                 if ( baseNamespace == null || namespace.startsWith( baseNamespace + "." ) )
1278                 {
1279                     int i = namespace.indexOf( '.', fromIndex );
1280                     if ( i >= 0 )
1281                     {
1282                         namespaces.add( namespace.substring( fromIndex, i ) );
1283                     }
1284                     else
1285                     {
1286                         namespaces.add( namespace.substring( fromIndex ) );
1287                     }
1288                 }
1289             }
1290             return new ArrayList<>( namespaces );
1291         }
1292         catch ( IOException e )
1293         {
1294             throw new MetadataResolutionException( e.getMessage(), e );
1295         }
1296     }
1297 
1298     @Override
1299     public Collection<String> getProjects( String repoId, String namespace )
1300         throws MetadataResolutionException
1301     {
1302         try
1303         {
1304             List<String> projects = new ArrayList<>();
1305             File directory = new File( getDirectory( repoId ), namespace );
1306             File[] files = directory.listFiles();
1307             if ( files != null )
1308             {
1309                 for ( File project : files )
1310                 {
1311                     if ( new File( project, PROJECT_METADATA_KEY + ".properties" ).exists() )
1312                     {
1313                         projects.add( project.getName() );
1314                     }
1315                 }
1316             }
1317             return projects;
1318         }
1319         catch ( IOException e )
1320         {
1321             throw new MetadataResolutionException( e.getMessage(), e );
1322         }
1323     }
1324 
1325     @Override
1326     public Collection<String> getProjectVersions( String repoId, String namespace, String projectId )
1327         throws MetadataResolutionException
1328     {
1329         try
1330         {
1331             List<String> projectVersions = new ArrayList<>();
1332             File directory = new File( getDirectory( repoId ), namespace + "/" + projectId );
1333             File[] files = directory.listFiles();
1334             if ( files != null )
1335             {
1336                 for ( File projectVersion : files )
1337                 {
1338                     if ( new File( projectVersion, PROJECT_VERSION_METADATA_KEY + ".properties" ).exists() )
1339                     {
1340                         projectVersions.add( projectVersion.getName() );
1341                     }
1342                 }
1343             }
1344             return projectVersions;
1345         }
1346         catch ( IOException e )
1347         {
1348             throw new MetadataResolutionException( e.getMessage(), e );
1349         }
1350     }
1351 
1352     @Override
1353     public void removeProject( String repositoryId, String namespace, String projectId )
1354         throws MetadataRepositoryException
1355     {
1356         try
1357         {
1358             File directory = new File( getDirectory( repositoryId ), namespace + "/" + projectId );
1359             FileUtils.deleteDirectory( directory );
1360         }
1361         catch ( IOException e )
1362         {
1363             throw new MetadataRepositoryException( e.getMessage(), e );
1364         }
1365     }
1366 
1367     @Override
1368     public void removeProjectVersion( String repoId, String namespace, String projectId, String projectVersion )
1369         throws MetadataRepositoryException
1370     {
1371         try
1372         {
1373             File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
1374             FileUtils.deleteDirectory( directory );
1375         }
1376         catch ( IOException e )
1377         {
1378             throw new MetadataRepositoryException( e.getMessage(), e );
1379         }
1380 
1381     }
1382 
1383     private void writeProperties( Properties properties, File directory, String propertiesKey )
1384         throws IOException
1385     {
1386         directory.mkdirs();
1387         try (OutputStream os = Files.newOutputStream( new File( directory, propertiesKey + ".properties" ).toPath() ))
1388         {
1389             properties.store( os, null );
1390         }
1391     }
1392 
1393     private static class ArtifactComparator
1394         implements Comparator<ArtifactMetadata>
1395     {
1396         @Override
1397         public int compare( ArtifactMetadata artifact1, ArtifactMetadata artifact2 )
1398         {
1399             if ( artifact1.getWhenGathered() == artifact2.getWhenGathered() )
1400             {
1401                 return 0;
1402             }
1403             if ( artifact1.getWhenGathered() == null )
1404             {
1405                 return 1;
1406             }
1407             if ( artifact2.getWhenGathered() == null )
1408             {
1409                 return -1;
1410             }
1411             return artifact1.getWhenGathered().compareTo( artifact2.getWhenGathered() );
1412         }
1413     }
1414 
1415     @Override
1416     public List<ArtifactMetadata> getArtifacts( String repoId )
1417         throws MetadataRepositoryException
1418     {
1419         try
1420         {
1421             List<ArtifactMetadata> artifacts = new ArrayList<>();
1422             for ( String ns : getRootNamespaces( repoId ) )
1423             {
1424                 getArtifacts( artifacts, repoId, ns );
1425             }
1426             return artifacts;
1427         }
1428         catch ( MetadataResolutionException e )
1429         {
1430             throw new MetadataRepositoryException( e.getMessage(), e );
1431         }
1432     }
1433 
1434     private void getArtifacts( List<ArtifactMetadata> artifacts, String repoId, String ns )
1435         throws MetadataResolutionException
1436     {
1437         for ( String namespace : getNamespaces( repoId, ns ) )
1438         {
1439             getArtifacts( artifacts, repoId, ns + "." + namespace );
1440         }
1441 
1442         for ( String project : getProjects( repoId, ns ) )
1443         {
1444             for ( String version : getProjectVersions( repoId, ns, project ) )
1445             {
1446                 for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) )
1447                 {
1448                     artifacts.add( artifact );
1449                 }
1450             }
1451         }
1452     }
1453 
1454     @Override
1455     public List<ArtifactMetadata> searchArtifacts( String text, String repositoryId, boolean exact )
1456         throws MetadataRepositoryException
1457     {
1458         throw new UnsupportedOperationException( "searchArtifacts not yet implemented in File backend" );
1459     }
1460 
1461     @Override
1462     public List<ArtifactMetadata> searchArtifacts( String key, String text, String repositoryId, boolean exact )
1463         throws MetadataRepositoryException
1464     {
1465         throw new UnsupportedOperationException( "searchArtifacts not yet implemented in File backend" );
1466     }
1467 }