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