]> source.dussan.org Git - archiva.git/blob
1211c2c9d8cf2949484094f92e5a4d3b60ba9832
[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 java.io.File;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.FileOutputStream;
26 import java.io.IOException;
27 import java.util.ArrayList;
28 import java.util.Arrays;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.Date;
32 import java.util.HashMap;
33 import java.util.HashSet;
34 import java.util.LinkedHashSet;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Properties;
38 import java.util.Set;
39 import java.util.StringTokenizer;
40
41 import org.apache.archiva.metadata.model.ArtifactMetadata;
42 import org.apache.archiva.metadata.model.CiManagement;
43 import org.apache.archiva.metadata.model.Dependency;
44 import org.apache.archiva.metadata.model.IssueManagement;
45 import org.apache.archiva.metadata.model.License;
46 import org.apache.archiva.metadata.model.MailingList;
47 import org.apache.archiva.metadata.model.MetadataFacet;
48 import org.apache.archiva.metadata.model.MetadataFacetFactory;
49 import org.apache.archiva.metadata.model.Organization;
50 import org.apache.archiva.metadata.model.ProjectMetadata;
51 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
52 import org.apache.archiva.metadata.model.ProjectVersionReference;
53 import org.apache.archiva.metadata.model.Scm;
54 import org.apache.archiva.metadata.repository.MetadataRepository;
55 import org.apache.commons.io.FileUtils;
56 import org.apache.commons.io.IOUtils;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60 /**
61  * @plexus.component role="org.apache.archiva.metadata.repository.MetadataRepository"
62  */
63 public class FileMetadataRepository
64     implements MetadataRepository
65 {
66     /**
67      * TODO: this isn't suitable for production use
68      *
69      * @plexus.configuration
70      */
71     private File directory = new File( System.getProperty( "user.home" ), ".archiva-metadata" );
72
73     /**
74      * @plexus.requirement role="org.apache.archiva.metadata.model.MetadataFacetFactory"
75      */
76     private Map<String, MetadataFacetFactory> metadataFacetFactories;
77
78     private static final Logger log = LoggerFactory.getLogger( FileMetadataRepository.class );
79
80     private static final String PROJECT_METADATA_KEY = "project-metadata";
81
82     private static final String PROJECT_VERSION_METADATA_KEY = "version-metadata";
83
84     private static final String NAMESPACE_METADATA_KEY = "namespace-metadata";
85
86     private static final String METADATA_KEY = "metadata";
87
88     public void updateProject( String repoId, ProjectMetadata project )
89     {
90         updateProject( repoId, project.getNamespace(), project.getId() );
91     }
92
93     private void updateProject( String repoId, String namespace, String id )
94     {
95         // TODO: this is a more braindead implementation than we would normally expect, for prototyping purposes
96         updateNamespace( repoId, namespace );
97
98         try
99         {
100             File namespaceDirectory = new File( this.directory, repoId + "/" + namespace );
101             Properties properties = new Properties();
102             properties.setProperty( "namespace", namespace );
103             properties.setProperty( "id", id );
104             writeProperties( properties, new File( namespaceDirectory, id ), PROJECT_METADATA_KEY );
105         }
106         catch ( IOException e )
107         {
108             // TODO!
109             e.printStackTrace();
110         }
111     }
112
113     public void updateProjectVersion( String repoId, String namespace, String projectId,
114                                       ProjectVersionMetadata versionMetadata )
115     {
116         updateProject( repoId, namespace, projectId );
117
118         File directory =
119             new File( this.directory, repoId + "/" + namespace + "/" + projectId + "/" + versionMetadata.getId() );
120
121         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
122         // remove properties that are not references or artifacts
123         for ( Object key : properties.keySet() )
124         {
125             String name = (String) key;
126             if ( !name.contains( ":" ) && !name.equals( "facetIds" ) )
127             {
128                 properties.remove( name );
129             }
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         {
138             setProperty( properties, "scm.connection", versionMetadata.getScm().getConnection() );
139             setProperty( properties, "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
140             setProperty( properties, "scm.url", versionMetadata.getScm().getUrl() );
141         }
142         if ( versionMetadata.getCiManagement() != null )
143         {
144             setProperty( properties, "ci.system", versionMetadata.getCiManagement().getSystem() );
145             setProperty( properties, "ci.url", versionMetadata.getCiManagement().getUrl() );
146         }
147         if ( versionMetadata.getIssueManagement() != null )
148         {
149             setProperty( properties, "issue.system", versionMetadata.getIssueManagement().getSystem() );
150             setProperty( properties, "issue.url", versionMetadata.getIssueManagement().getUrl() );
151         }
152         if ( versionMetadata.getOrganization() != null )
153         {
154             setProperty( properties, "org.name", versionMetadata.getOrganization().getName() );
155             setProperty( properties, "org.url", versionMetadata.getOrganization().getUrl() );
156         }
157         int i = 0;
158         for ( License license : versionMetadata.getLicenses() )
159         {
160             setProperty( properties, "license." + i + ".name", license.getName() );
161             setProperty( properties, "license." + i + ".url", license.getUrl() );
162             i++;
163         }
164         i = 0;
165         for ( MailingList mailingList : versionMetadata.getMailingLists() )
166         {
167             setProperty( properties, "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
168             setProperty( properties, "mailingList." + i + ".name", mailingList.getName() );
169             setProperty( properties, "mailingList." + i + ".post", mailingList.getPostAddress() );
170             setProperty( properties, "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
171             setProperty( properties, "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
172             setProperty( properties, "mailingList." + i + ".otherArchives", join( mailingList.getOtherArchives() ) );
173             i++;
174         }
175         i = 0;
176         for ( Dependency dependency : versionMetadata.getDependencies() )
177         {
178             setProperty( properties, "dependency." + i + ".classifier", dependency.getClassifier() );
179             setProperty( properties, "dependency." + i + ".scope", dependency.getScope() );
180             setProperty( properties, "dependency." + i + ".systemPath", dependency.getSystemPath() );
181             setProperty( properties, "dependency." + i + ".artifactId", dependency.getArtifactId() );
182             setProperty( properties, "dependency." + i + ".groupId", dependency.getGroupId() );
183             setProperty( properties, "dependency." + i + ".version", dependency.getVersion() );
184             setProperty( properties, "dependency." + i + ".type", dependency.getType() );
185             i++;
186         }
187         Set<String> facetIds = new LinkedHashSet<String>( versionMetadata.getFacetIds() );
188         facetIds.addAll( Arrays.asList( properties.getProperty( "facetIds", "" ).split( "," ) ) );
189         properties.setProperty( "facetIds", join( facetIds ) );
190
191         for ( MetadataFacet facet : versionMetadata.getFacetList() )
192         {
193             properties.putAll( facet.toProperties() );
194         }
195
196         try
197         {
198             writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
199         }
200         catch ( IOException e )
201         {
202             // TODO
203             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
204         }
205     }
206
207     public void updateProjectReference( String repoId, String namespace, String projectId, String projectVersion,
208                                         ProjectVersionReference reference )
209     {
210         File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId + "/" + projectVersion );
211
212         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
213         int i = Integer.valueOf( properties.getProperty( "ref:lastReferenceNum", "-1" ) ) + 1;
214         setProperty( properties, "ref:lastReferenceNum", Integer.toString( i ) );
215         setProperty( properties, "ref:reference." + i + ".namespace", reference.getNamespace() );
216         setProperty( properties, "ref:reference." + i + ".projectId", reference.getProjectId() );
217         setProperty( properties, "ref:reference." + i + ".projectVersion", reference.getProjectVersion() );
218         setProperty( properties, "ref:reference." + i + ".referenceType", reference.getReferenceType().toString() );
219
220         try
221         {
222             writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
223         }
224         catch ( IOException e )
225         {
226             // TODO
227             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
228         }
229     }
230
231     public void updateNamespace( String repoId, String namespace )
232     {
233         try
234         {
235             File namespaceDirectory = new File( this.directory, repoId + "/" + namespace );
236             Properties properties = new Properties();
237             properties.setProperty( "namespace", namespace );
238             writeProperties( properties, namespaceDirectory, NAMESPACE_METADATA_KEY );
239
240         }
241         catch ( IOException e )
242         {
243             // TODO!
244             e.printStackTrace();
245         }
246     }
247
248     public List<String> getMetadataFacets( String repoId, String facetId )
249     {
250         File directory = getMetadataDirectory( repoId, facetId );
251         List<String> facets = new ArrayList<String>();
252         recurse( facets, "", directory );
253         return facets;
254     }
255
256     private void recurse( List<String> facets, String prefix, File directory )
257     {
258         File[] list = directory.listFiles();
259         if ( list != null )
260         {
261             for ( File dir : list )
262             {
263                 if ( dir.isDirectory() )
264                 {
265                     recurse( facets, prefix + "/" + dir.getName(), dir );
266                 }
267                 else if ( dir.getName().equals( METADATA_KEY + ".properties" ) )
268                 {
269                     facets.add( prefix.substring( 1 ) );
270                 }
271             }
272         }
273     }
274
275     public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
276     {
277         Properties properties;
278         try
279         {
280             properties =
281                 readProperties( new File( getMetadataDirectory( repositoryId, facetId ), name ), METADATA_KEY );
282         }
283         catch ( FileNotFoundException e )
284         {
285             return null;
286         }
287         catch ( IOException e )
288         {
289             // TODO
290             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
291             return null;
292         }
293         MetadataFacet metadataFacet = null;
294         MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
295         if ( metadataFacetFactory != null )
296         {
297             metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
298             Map<String, String> map = new HashMap<String, String>();
299             for ( Object key : properties.keySet() )
300             {
301                 String property = (String) key;
302                 map.put( property, properties.getProperty( property ) );
303             }
304             metadataFacet.fromProperties( map );
305         }
306         return metadataFacet;
307     }
308
309     public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
310     {
311         Properties properties = new Properties();
312         properties.putAll( metadataFacet.toProperties() );
313
314         try
315         {
316             File directory =
317                 new File( getMetadataDirectory( repositoryId, metadataFacet.getFacetId() ), metadataFacet.getName() );
318             writeProperties( properties, directory, METADATA_KEY );
319         }
320         catch ( IOException e )
321         {
322             // TODO!
323             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
324         }
325     }
326
327     public void removeMetadataFacets( String repositoryId, String facetId )
328     {
329         try
330         {
331             FileUtils.deleteDirectory( getMetadataDirectory( repositoryId, facetId ) );
332         }
333         catch ( IOException e )
334         {
335             // TODO!
336             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
337         }
338     }
339
340     public void removeMetadataFacet( String repoId, String facetId, String name )
341     {
342         File dir = new File( getMetadataDirectory( repoId, facetId ), name );
343         try
344         {
345             FileUtils.deleteDirectory( dir );
346         }
347         catch ( IOException e )
348         {
349             // TODO
350             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
351         }
352     }
353
354     public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
355     {
356         // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
357         //  of this information (eg. in Lucene, as before)
358
359         List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
360         for ( String ns : getRootNamespaces( repoId ) )
361         {
362             getArtifactsByDateRange( artifacts, repoId, ns, startTime, endTime );
363         }
364         return artifacts;
365     }
366
367     private void getArtifactsByDateRange( List<ArtifactMetadata> artifacts, String repoId, String ns, Date startTime,
368                                           Date endTime )
369     {
370         for ( String namespace : getNamespaces( repoId, ns ) )
371         {
372             getArtifactsByDateRange( artifacts, repoId, ns + "." + namespace, startTime, endTime );
373         }
374
375         for ( String project : getProjects( repoId, ns ) )
376         {
377             for ( String version : getProjectVersions( repoId, ns, project ) )
378             {
379                 for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) )
380                 {
381                     if ( startTime == null || startTime.before( artifact.getWhenGathered() ) )
382                     {
383                         if ( endTime == null || endTime.after( artifact.getWhenGathered() ) )
384                         {
385                             artifacts.add( artifact );
386                         }
387                     }
388                 }
389             }
390         }
391     }
392
393     public Collection<ArtifactMetadata> getArtifacts( String repoId, String namespace, String projectId,
394                                                       String projectVersion )
395     {
396         Map<String, ArtifactMetadata> artifacts = new HashMap<String, ArtifactMetadata>();
397
398         File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId + "/" + projectVersion );
399
400         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
401
402         for ( Map.Entry entry : properties.entrySet() )
403         {
404             String name = (String) entry.getKey();
405             StringTokenizer tok = new StringTokenizer( name, ":" );
406             if ( tok.hasMoreTokens() && "artifact".equals( tok.nextToken() ) )
407             {
408                 String field = tok.nextToken();
409                 String id = tok.nextToken();
410
411                 ArtifactMetadata artifact = artifacts.get( id );
412                 if ( artifact == null )
413                 {
414                     artifact = new ArtifactMetadata();
415                     artifact.setRepositoryId( repoId );
416                     artifact.setNamespace( namespace );
417                     artifact.setProject( projectId );
418                     artifact.setVersion( projectVersion );
419                     artifact.setId( id );
420                     artifacts.put( id, artifact );
421                 }
422
423                 String value = (String) entry.getValue();
424                 if ( "updated".equals( field ) )
425                 {
426                     artifact.setFileLastModified( Long.valueOf( value ) );
427                 }
428                 else if ( "size".equals( field ) )
429                 {
430                     artifact.setSize( Long.valueOf( value ) );
431                 }
432                 else if ( "whenGathered".equals( field ) )
433                 {
434                     artifact.setWhenGathered( new Date( Long.valueOf( value ) ) );
435                 }
436                 else if ( "version".equals( field ) )
437                 {
438                     artifact.setVersion( value );
439                 }
440                 else if ( "md5".equals( field ) )
441                 {
442                     artifact.setMd5( value );
443                 }
444                 else if ( "sha1".equals( field ) )
445                 {
446                     artifact.setSha1( value );
447                 }
448             }
449         }
450         return artifacts.values();
451     }
452
453     public Collection<String> getRepositories()
454     {
455         String[] repoIds = this.directory.list();
456         return repoIds != null ? Arrays.asList( repoIds ) : Collections.<String>emptyList();
457     }
458
459     public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
460     {
461         // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
462         //  of this information (eg. in Lucene, as before)
463         // alternatively, we could build a referential tree in the content repository, however it would need some levels
464         // of depth to avoid being too broad to be useful (eg. /repository/checksums/a/ab/abcdef1234567)
465
466         List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
467         for ( String ns : getRootNamespaces( repositoryId ) )
468         {
469             getArtifactsByChecksum( artifacts, repositoryId, ns, checksum );
470         }
471         return artifacts;
472     }
473
474     public void deleteArtifact( String repositoryId, String namespace, String project, String version, String id )
475     {
476         File directory = new File( this.directory, repositoryId + "/" + namespace + "/" + project + "/" + version );
477
478         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
479
480         properties.remove( "artifact:updated:" + id );
481         properties.remove( "artifact:whenGathered:" + id );
482         properties.remove( "artifact:size:" + id );
483         properties.remove( "artifact:md5:" + id );
484         properties.remove( "artifact:sha1:" + id );
485         properties.remove( "artifact:version:" + id );
486
487         try
488         {
489             writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
490         }
491         catch ( IOException e )
492         {
493             // TODO
494             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
495         }
496     }
497
498     public void deleteRepository( String repoId )
499     {
500         File directory = new File( this.directory, repoId );
501
502         try
503         {
504             FileUtils.deleteDirectory( directory );
505         }
506         catch ( IOException e )
507         {
508             // TODO
509             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
510         }
511     }
512
513     private void getArtifactsByChecksum( List<ArtifactMetadata> artifacts, String repositoryId, String ns,
514                                          String checksum )
515     {
516         for ( String namespace : getNamespaces( repositoryId, ns ) )
517         {
518             getArtifactsByChecksum( artifacts, repositoryId, ns + "." + namespace, checksum );
519         }
520
521         for ( String project : getProjects( repositoryId, ns ) )
522         {
523             for ( String version : getProjectVersions( repositoryId, ns, project ) )
524             {
525                 for ( ArtifactMetadata artifact : getArtifacts( repositoryId, ns, project, version ) )
526                 {
527                     if ( checksum.equals( artifact.getMd5() ) || checksum.equals( artifact.getSha1() ) )
528                     {
529                         artifacts.add( artifact );
530                     }
531                 }
532             }
533         }
534     }
535
536     private File getMetadataDirectory( String repositoryId, String facetId )
537     {
538         return new File( this.directory, repositoryId + "/.meta/" + facetId );
539     }
540
541     private String join( Collection<String> ids )
542     {
543         if ( !ids.isEmpty() )
544         {
545             StringBuilder s = new StringBuilder();
546             for ( String id : ids )
547             {
548                 s.append( id );
549                 s.append( "," );
550             }
551             return s.substring( 0, s.length() - 1 );
552         }
553         return "";
554     }
555
556     private void setProperty( Properties properties, String name, String value )
557     {
558         if ( value != null )
559         {
560             properties.setProperty( name, value );
561         }
562     }
563
564     public void updateArtifact( String repoId, String namespace, String projectId, String projectVersion,
565                                 ArtifactMetadata artifact )
566     {
567         File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId + "/" + projectVersion );
568
569         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
570
571         properties.setProperty( "artifact:updated:" + artifact.getId(),
572                                 Long.toString( artifact.getFileLastModified().getTime() ) );
573         properties.setProperty( "artifact:whenGathered:" + artifact.getId(),
574                                 Long.toString( artifact.getWhenGathered().getTime() ) );
575         properties.setProperty( "artifact:size:" + artifact.getId(), Long.toString( artifact.getSize() ) );
576         if ( artifact.getMd5() != null )
577         {
578             properties.setProperty( "artifact:md5:" + artifact.getId(), artifact.getMd5() );
579         }
580         if ( artifact.getSha1() != null )
581         {
582             properties.setProperty( "artifact:sha1:" + artifact.getId(), artifact.getSha1() );
583         }
584         properties.setProperty( "artifact:version:" + artifact.getId(), artifact.getVersion() );
585
586         try
587         {
588             writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
589         }
590         catch ( IOException e )
591         {
592             // TODO
593             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
594         }
595     }
596
597     private Properties readOrCreateProperties( File directory, String propertiesKey )
598     {
599         try
600         {
601             return readProperties( directory, propertiesKey );
602         }
603         catch ( FileNotFoundException e )
604         {
605             // ignore and return new properties
606         }
607         catch ( IOException e )
608         {
609             // TODO
610             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
611         }
612         return new Properties();
613     }
614
615     private Properties readProperties( File directory, String propertiesKey )
616         throws IOException
617     {
618         Properties properties = new Properties();
619         FileInputStream in = null;
620         try
621         {
622             in = new FileInputStream( new File( directory, propertiesKey + ".properties" ) );
623             properties.load( in );
624         }
625         finally
626         {
627             IOUtils.closeQuietly( in );
628         }
629         return properties;
630     }
631
632     public ProjectMetadata getProject( String repoId, String namespace, String projectId )
633     {
634         File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId );
635
636         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
637
638         ProjectMetadata project = new ProjectMetadata();
639         project.setNamespace( properties.getProperty( "namespace" ) );
640         project.setId( properties.getProperty( "id" ) );
641         return project;
642     }
643
644     public ProjectVersionMetadata getProjectVersion( String repoId, String namespace, String projectId,
645                                                      String projectVersion )
646     {
647         File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId + "/" + projectVersion );
648
649         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
650         String id = properties.getProperty( "id" );
651         ProjectVersionMetadata versionMetadata = null;
652         if ( id != null )
653         {
654             versionMetadata = new ProjectVersionMetadata();
655             versionMetadata.setId( id );
656             versionMetadata.setName( properties.getProperty( "name" ) );
657             versionMetadata.setDescription( properties.getProperty( "description" ) );
658             versionMetadata.setUrl( properties.getProperty( "url" ) );
659             versionMetadata.setIncomplete( Boolean.valueOf( properties.getProperty( "incomplete", "false" ) ) );
660
661             String scmConnection = properties.getProperty( "scm.connection" );
662             String scmDeveloperConnection = properties.getProperty( "scm.developerConnection" );
663             String scmUrl = properties.getProperty( "scm.url" );
664             if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
665             {
666                 Scm scm = new Scm();
667                 scm.setConnection( scmConnection );
668                 scm.setDeveloperConnection( scmDeveloperConnection );
669                 scm.setUrl( scmUrl );
670                 versionMetadata.setScm( scm );
671             }
672
673             String ciSystem = properties.getProperty( "ci.system" );
674             String ciUrl = properties.getProperty( "ci.url" );
675             if ( ciSystem != null || ciUrl != null )
676             {
677                 CiManagement ci = new CiManagement();
678                 ci.setSystem( ciSystem );
679                 ci.setUrl( ciUrl );
680                 versionMetadata.setCiManagement( ci );
681             }
682
683             String issueSystem = properties.getProperty( "issue.system" );
684             String issueUrl = properties.getProperty( "issue.url" );
685             if ( issueSystem != null || issueUrl != null )
686             {
687                 IssueManagement issueManagement = new IssueManagement();
688                 issueManagement.setSystem( issueSystem );
689                 issueManagement.setUrl( issueUrl );
690                 versionMetadata.setIssueManagement( issueManagement );
691             }
692
693             String orgName = properties.getProperty( "org.name" );
694             String orgUrl = properties.getProperty( "org.url" );
695             if ( orgName != null || orgUrl != null )
696             {
697                 Organization org = new Organization();
698                 org.setName( orgName );
699                 org.setUrl( orgUrl );
700                 versionMetadata.setOrganization( org );
701             }
702
703             boolean done = false;
704             int i = 0;
705             while ( !done )
706             {
707                 String licenseName = properties.getProperty( "license." + i + ".name" );
708                 String licenseUrl = properties.getProperty( "license." + i + ".url" );
709                 if ( licenseName != null || licenseUrl != null )
710                 {
711                     License license = new License();
712                     license.setName( licenseName );
713                     license.setUrl( licenseUrl );
714                     versionMetadata.addLicense( license );
715                 }
716                 else
717                 {
718                     done = true;
719                 }
720                 i++;
721             }
722
723             done = false;
724             i = 0;
725             while ( !done )
726             {
727                 String mailingListName = properties.getProperty( "mailingList." + i + ".name" );
728                 if ( mailingListName != null )
729                 {
730                     MailingList mailingList = new MailingList();
731                     mailingList.setName( mailingListName );
732                     mailingList.setMainArchiveUrl( properties.getProperty( "mailingList." + i + ".archive" ) );
733                     mailingList.setOtherArchives(
734                         Arrays.asList( properties.getProperty( "mailingList." + i + ".otherArchives" ).split( "," ) ) );
735                     mailingList.setPostAddress( properties.getProperty( "mailingList." + i + ".post" ) );
736                     mailingList.setSubscribeAddress( properties.getProperty( "mailingList." + i + ".subscribe" ) );
737                     mailingList.setUnsubscribeAddress( properties.getProperty( "mailingList." + i + ".unsubscribe" ) );
738                     versionMetadata.addMailingList( mailingList );
739                 }
740                 else
741                 {
742                     done = true;
743                 }
744                 i++;
745             }
746
747             done = false;
748             i = 0;
749             while ( !done )
750             {
751                 String dependencyArtifactId = properties.getProperty( "dependency." + i + ".artifactId" );
752                 if ( dependencyArtifactId != null )
753                 {
754                     Dependency dependency = new Dependency();
755                     dependency.setArtifactId( dependencyArtifactId );
756                     dependency.setGroupId( properties.getProperty( "dependency." + i + ".groupId" ) );
757                     dependency.setClassifier( properties.getProperty( "dependency." + i + ".classifier" ) );
758                     dependency.setOptional(
759                         Boolean.valueOf( properties.getProperty( "dependency." + i + ".optional" ) ) );
760                     dependency.setScope( properties.getProperty( "dependency." + i + ".scope" ) );
761                     dependency.setSystemPath( properties.getProperty( "dependency." + i + ".systemPath" ) );
762                     dependency.setType( properties.getProperty( "dependency." + i + ".type" ) );
763                     dependency.setVersion( properties.getProperty( "dependency." + i + ".version" ) );
764                     versionMetadata.addDependency( dependency );
765                 }
766                 else
767                 {
768                     done = true;
769                 }
770                 i++;
771             }
772
773             String facetIds = properties.getProperty( "facetIds", "" );
774             if ( facetIds.length() > 0 )
775             {
776                 for ( String facetId : facetIds.split( "," ) )
777                 {
778                     MetadataFacetFactory factory = metadataFacetFactories.get( facetId );
779                     if ( factory == null )
780                     {
781                         log.error( "Attempted to load unknown metadata facet: " + facetId );
782                     }
783                     else
784                     {
785                         MetadataFacet facet = factory.createMetadataFacet();
786                         Map<String, String> map = new HashMap<String, String>();
787                         for ( Object key : properties.keySet() )
788                         {
789                             String property = (String) key;
790                             if ( property.startsWith( facet.getFacetId() ) )
791                             {
792                                 map.put( property, properties.getProperty( property ) );
793                             }
794                         }
795                         facet.fromProperties( map );
796                         versionMetadata.addFacet( facet );
797                     }
798                 }
799             }
800
801             for ( MetadataFacet facet : versionMetadata.getFacetList() )
802             {
803                 properties.putAll( facet.toProperties() );
804             }
805         }
806         return versionMetadata;
807     }
808
809     public Collection<String> getArtifactVersions( String repoId, String namespace, String projectId,
810                                                    String projectVersion )
811     {
812         File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId + "/" + projectVersion );
813
814         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
815
816         Set<String> versions = new HashSet<String>();
817         for ( Map.Entry entry : properties.entrySet() )
818         {
819             String name = (String) entry.getKey();
820             if ( name.startsWith( "artifact:version:" ) )
821             {
822                 versions.add( (String) entry.getValue() );
823             }
824         }
825         return versions;
826     }
827
828     public Collection<ProjectVersionReference> getProjectReferences( String repoId, String namespace, String projectId,
829                                                                      String projectVersion )
830     {
831         File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId + "/" + projectVersion );
832
833         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
834         int numberOfRefs = Integer.valueOf( properties.getProperty( "ref:lastReferenceNum", "-1" ) ) + 1;
835
836         List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
837         for ( int i = 0; i < numberOfRefs; i++ )
838         {
839             ProjectVersionReference reference = new ProjectVersionReference();
840             reference.setProjectId( properties.getProperty( "ref:reference." + i + ".projectId" ) );
841             reference.setNamespace( properties.getProperty( "ref:reference." + i + ".namespace" ) );
842             reference.setProjectVersion( properties.getProperty( "ref:reference." + i + ".projectVersion" ) );
843             reference.setReferenceType( ProjectVersionReference.ReferenceType.valueOf(
844                 properties.getProperty( "ref:reference." + i + ".referenceType" ) ) );
845             references.add( reference );
846         }
847         return references;
848     }
849
850     public Collection<String> getRootNamespaces( String repoId )
851     {
852         return getNamespaces( repoId, null );
853     }
854
855     public Collection<String> getNamespaces( String repoId, String baseNamespace )
856     {
857         List<String> allNamespaces = new ArrayList<String>();
858         File directory = new File( this.directory, repoId );
859         File[] files = directory.listFiles();
860         if ( files != null )
861         {
862             for ( File namespace : files )
863             {
864                 if ( new File( namespace, NAMESPACE_METADATA_KEY + ".properties" ).exists() )
865                 {
866                     allNamespaces.add( namespace.getName() );
867                 }
868             }
869         }
870
871         Set<String> namespaces = new LinkedHashSet<String>();
872         int fromIndex = baseNamespace != null ? baseNamespace.length() + 1 : 0;
873         for ( String namespace : allNamespaces )
874         {
875             if ( baseNamespace == null || namespace.startsWith( baseNamespace + "." ) )
876             {
877                 int i = namespace.indexOf( '.', fromIndex );
878                 if ( i >= 0 )
879                 {
880                     namespaces.add( namespace.substring( fromIndex, i ) );
881                 }
882                 else
883                 {
884                     namespaces.add( namespace.substring( fromIndex ) );
885                 }
886             }
887         }
888         return new ArrayList<String>( namespaces );
889     }
890
891     public Collection<String> getProjects( String repoId, String namespace )
892     {
893         List<String> projects = new ArrayList<String>();
894         File directory = new File( this.directory, repoId + "/" + namespace );
895         File[] files = directory.listFiles();
896         if ( files != null )
897         {
898             for ( File project : files )
899             {
900                 if ( new File( project, PROJECT_METADATA_KEY + ".properties" ).exists() )
901                 {
902                     projects.add( project.getName() );
903                 }
904             }
905         }
906         return projects;
907     }
908
909     public Collection<String> getProjectVersions( String repoId, String namespace, String projectId )
910     {
911         List<String> projectVersions = new ArrayList<String>();
912         File directory = new File( this.directory, repoId + "/" + namespace + "/" + projectId );
913         File[] files = directory.listFiles();
914         if ( files != null )
915         {
916             for ( File projectVersion : files )
917             {
918                 if ( new File( projectVersion, PROJECT_VERSION_METADATA_KEY + ".properties" ).exists() )
919                 {
920                     projectVersions.add( projectVersion.getName() );
921                 }
922             }
923         }
924         return projectVersions;
925     }
926
927     private void writeProperties( Properties properties, File directory, String propertiesKey )
928         throws IOException
929     {
930         directory.mkdirs();
931         FileOutputStream os = new FileOutputStream( new File( directory, propertiesKey + ".properties" ) );
932         try
933         {
934             properties.store( os, null );
935         }
936         finally
937         {
938             IOUtils.closeQuietly( os );
939         }
940     }
941
942     public void setDirectory( File directory )
943     {
944         this.directory = directory;
945     }
946
947     public void setMetadataFacetFactories( Map<String, MetadataFacetFactory> metadataFacetFactories )
948     {
949         this.metadataFacetFactories = metadataFacetFactories;
950     }
951 }