]> source.dussan.org Git - archiva.git/blob
bab991619f37788d2bc92a41ae760e0c02fe741d
[archiva.git] /
1 package org.apache.archiva.metadata.repository.file;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *   http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import org.apache.archiva.metadata.model.ArtifactMetadata;
23 import org.apache.archiva.metadata.model.CiManagement;
24 import org.apache.archiva.metadata.model.Dependency;
25 import org.apache.archiva.metadata.model.IssueManagement;
26 import org.apache.archiva.metadata.model.License;
27 import org.apache.archiva.metadata.model.MailingList;
28 import org.apache.archiva.metadata.model.MetadataFacet;
29 import org.apache.archiva.metadata.model.MetadataFacetFactory;
30 import org.apache.archiva.metadata.model.Organization;
31 import org.apache.archiva.metadata.model.ProjectMetadata;
32 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
33 import org.apache.archiva.metadata.model.ProjectVersionReference;
34 import org.apache.archiva.metadata.model.Scm;
35 import org.apache.archiva.metadata.repository.MetadataRepository;
36 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
37 import org.apache.commons.io.FileUtils;
38 import org.apache.commons.io.IOUtils;
39 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
42
43 import java.io.File;
44 import java.io.FileInputStream;
45 import java.io.FileNotFoundException;
46 import java.io.FileOutputStream;
47 import java.io.IOException;
48 import java.util.ArrayList;
49 import java.util.Arrays;
50 import java.util.Collection;
51 import java.util.Collections;
52 import java.util.Comparator;
53 import java.util.Date;
54 import java.util.HashMap;
55 import java.util.HashSet;
56 import java.util.LinkedHashSet;
57 import java.util.List;
58 import java.util.Map;
59 import java.util.Properties;
60 import java.util.Set;
61 import java.util.StringTokenizer;
62
63 public class FileMetadataRepository
64     implements MetadataRepository
65 {
66     private final Map<String, MetadataFacetFactory> metadataFacetFactories;
67
68     private final ArchivaConfiguration configuration;
69
70     private static final Logger log = LoggerFactory.getLogger( FileMetadataRepository.class );
71
72     private static final String PROJECT_METADATA_KEY = "project-metadata";
73
74     private static final String PROJECT_VERSION_METADATA_KEY = "version-metadata";
75
76     private static final String NAMESPACE_METADATA_KEY = "namespace-metadata";
77
78     private static final String METADATA_KEY = "metadata";
79
80     public FileMetadataRepository( Map<String, MetadataFacetFactory> metadataFacetFactories,
81                                    ArchivaConfiguration configuration )
82     {
83         this.metadataFacetFactories = metadataFacetFactories;
84         this.configuration = configuration;
85     }
86
87     private File getBaseDirectory( String repoId )
88     {
89         // TODO: should be configurable, like the index
90         String basedir = configuration.getConfiguration().getManagedRepositoriesAsMap().get( repoId ).getLocation();
91         File dir = new File( basedir, ".archiva" );
92         return dir;
93     }
94
95     private File getDirectory( String repoId )
96     {
97         return new File( getBaseDirectory( repoId ), "content" );
98     }
99
100     public void updateProject( String repoId, ProjectMetadata project )
101     {
102         updateProject( repoId, project.getNamespace(), project.getId() );
103     }
104
105     private void updateProject( String repoId, String namespace, String id )
106     {
107         // TODO: this is a more braindead implementation than we would normally expect, for prototyping purposes
108         updateNamespace( repoId, namespace );
109
110         try
111         {
112             File namespaceDirectory = new File( getDirectory( repoId ), namespace );
113             Properties properties = new Properties();
114             properties.setProperty( "namespace", namespace );
115             properties.setProperty( "id", id );
116             writeProperties( properties, new File( namespaceDirectory, id ), PROJECT_METADATA_KEY );
117         }
118         catch ( IOException e )
119         {
120             // TODO!
121             e.printStackTrace();
122         }
123     }
124
125     public void updateProjectVersion( String repoId, String namespace, String projectId,
126                                       ProjectVersionMetadata versionMetadata )
127     {
128         updateProject( repoId, namespace, projectId );
129
130         File directory = new File( getDirectory( repoId ),
131                                    namespace + "/" + projectId + "/" + versionMetadata.getId() );
132
133         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
134         // remove properties that are not references or artifacts
135         for ( Object key : new ArrayList<Object>( properties.keySet() ) )
136         {
137             String name = (String) key;
138             if ( !name.contains( ":" ) && !name.equals( "facetIds" ) )
139             {
140                 properties.remove( name );
141             }
142
143             // clear the facet contents so old properties are no longer written
144             clearMetadataFacetProperties( versionMetadata.getFacetList(), properties, "" );
145         }
146         properties.setProperty( "id", versionMetadata.getId() );
147         setProperty( properties, "name", versionMetadata.getName() );
148         setProperty( properties, "description", versionMetadata.getDescription() );
149         setProperty( properties, "url", versionMetadata.getUrl() );
150         setProperty( properties, "incomplete", String.valueOf( versionMetadata.isIncomplete() ) );
151         if ( versionMetadata.getScm() != null )
152         {
153             setProperty( properties, "scm.connection", versionMetadata.getScm().getConnection() );
154             setProperty( properties, "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
155             setProperty( properties, "scm.url", versionMetadata.getScm().getUrl() );
156         }
157         if ( versionMetadata.getCiManagement() != null )
158         {
159             setProperty( properties, "ci.system", versionMetadata.getCiManagement().getSystem() );
160             setProperty( properties, "ci.url", versionMetadata.getCiManagement().getUrl() );
161         }
162         if ( versionMetadata.getIssueManagement() != null )
163         {
164             setProperty( properties, "issue.system", versionMetadata.getIssueManagement().getSystem() );
165             setProperty( properties, "issue.url", versionMetadata.getIssueManagement().getUrl() );
166         }
167         if ( versionMetadata.getOrganization() != null )
168         {
169             setProperty( properties, "org.name", versionMetadata.getOrganization().getName() );
170             setProperty( properties, "org.url", versionMetadata.getOrganization().getUrl() );
171         }
172         int i = 0;
173         for ( License license : versionMetadata.getLicenses() )
174         {
175             setProperty( properties, "license." + i + ".name", license.getName() );
176             setProperty( properties, "license." + i + ".url", license.getUrl() );
177             i++;
178         }
179         i = 0;
180         for ( MailingList mailingList : versionMetadata.getMailingLists() )
181         {
182             setProperty( properties, "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
183             setProperty( properties, "mailingList." + i + ".name", mailingList.getName() );
184             setProperty( properties, "mailingList." + i + ".post", mailingList.getPostAddress() );
185             setProperty( properties, "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
186             setProperty( properties, "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
187             setProperty( properties, "mailingList." + i + ".otherArchives", join( mailingList.getOtherArchives() ) );
188             i++;
189         }
190         i = 0;
191         for ( Dependency dependency : versionMetadata.getDependencies() )
192         {
193             setProperty( properties, "dependency." + i + ".classifier", dependency.getClassifier() );
194             setProperty( properties, "dependency." + i + ".scope", dependency.getScope() );
195             setProperty( properties, "dependency." + i + ".systemPath", dependency.getSystemPath() );
196             setProperty( properties, "dependency." + i + ".artifactId", dependency.getArtifactId() );
197             setProperty( properties, "dependency." + i + ".groupId", dependency.getGroupId() );
198             setProperty( properties, "dependency." + i + ".version", dependency.getVersion() );
199             setProperty( properties, "dependency." + i + ".type", dependency.getType() );
200             i++;
201         }
202         Set<String> facetIds = new LinkedHashSet<String>( versionMetadata.getFacetIds() );
203         facetIds.addAll( Arrays.asList( properties.getProperty( "facetIds", "" ).split( "," ) ) );
204         properties.setProperty( "facetIds", join( facetIds ) );
205
206         updateProjectVersionFacets( versionMetadata, properties );
207
208         try
209         {
210             writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
211         }
212         catch ( IOException e )
213         {
214             // TODO
215             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
216         }
217     }
218
219     private void updateProjectVersionFacets( ProjectVersionMetadata versionMetadata, Properties properties )
220     {
221         for ( MetadataFacet facet : versionMetadata.getFacetList() )
222         {
223             for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
224             {
225                 properties.setProperty( facet.getFacetId() + ":" + entry.getKey(), entry.getValue() );
226             }
227         }
228     }
229
230     private static void clearMetadataFacetProperties( Collection<MetadataFacet> facetList, Properties properties,
231                                                       String prefix )
232     {
233         List<Object> propsToRemove = new ArrayList<Object>();
234         for ( MetadataFacet facet : facetList )
235         {
236             for ( Object key : properties.keySet() )
237             {
238                 String keyString = (String) key;
239                 if ( keyString.startsWith( prefix + facet.getFacetId() + ":" ) )
240                 {
241                     propsToRemove.add( key );
242                 }
243             }
244         }
245
246         for ( Object key : propsToRemove )
247         {
248             properties.remove( key );
249         }
250     }
251
252     public void updateProjectReference( String repoId, String namespace, String projectId, String projectVersion,
253                                         ProjectVersionReference reference )
254     {
255         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
256
257         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
258         int i = Integer.parseInt( properties.getProperty( "ref:lastReferenceNum", "-1" ) ) + 1;
259         setProperty( properties, "ref:lastReferenceNum", Integer.toString( i ) );
260         setProperty( properties, "ref:reference." + i + ".namespace", reference.getNamespace() );
261         setProperty( properties, "ref:reference." + i + ".projectId", reference.getProjectId() );
262         setProperty( properties, "ref:reference." + i + ".projectVersion", reference.getProjectVersion() );
263         setProperty( properties, "ref:reference." + i + ".referenceType", reference.getReferenceType().toString() );
264
265         try
266         {
267             writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
268         }
269         catch ( IOException e )
270         {
271             // TODO
272             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
273         }
274     }
275
276     public void updateNamespace( String repoId, String namespace )
277     {
278         try
279         {
280             File namespaceDirectory = new File( getDirectory( repoId ), namespace );
281             Properties properties = new Properties();
282             properties.setProperty( "namespace", namespace );
283             writeProperties( properties, namespaceDirectory, NAMESPACE_METADATA_KEY );
284
285         }
286         catch ( IOException e )
287         {
288             // TODO!
289             e.printStackTrace();
290         }
291     }
292
293     public List<String> getMetadataFacets( String repoId, String facetId )
294     {
295         File directory = getMetadataDirectory( repoId, facetId );
296         List<String> facets = new ArrayList<String>();
297         recurse( facets, "", directory );
298         return facets;
299     }
300
301     private void recurse( List<String> facets, String prefix, File directory )
302     {
303         File[] list = directory.listFiles();
304         if ( list != null )
305         {
306             for ( File dir : list )
307             {
308                 if ( dir.isDirectory() )
309                 {
310                     recurse( facets, prefix + "/" + dir.getName(), dir );
311                 }
312                 else if ( dir.getName().equals( METADATA_KEY + ".properties" ) )
313                 {
314                     facets.add( prefix.substring( 1 ) );
315                 }
316             }
317         }
318     }
319
320     public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
321     {
322         Properties properties;
323         try
324         {
325             properties = readProperties( new File( getMetadataDirectory( repositoryId, facetId ), name ),
326                                          METADATA_KEY );
327         }
328         catch ( FileNotFoundException e )
329         {
330             return null;
331         }
332         catch ( IOException e )
333         {
334             // TODO
335             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
336             return null;
337         }
338         MetadataFacet metadataFacet = null;
339         MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
340         if ( metadataFacetFactory != null )
341         {
342             metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
343             Map<String, String> map = new HashMap<String, String>();
344             for ( Object key : new ArrayList( properties.keySet() ) )
345             {
346                 String property = (String) key;
347                 map.put( property, properties.getProperty( property ) );
348             }
349             metadataFacet.fromProperties( map );
350         }
351         return metadataFacet;
352     }
353
354     public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
355     {
356         Properties properties = new Properties();
357         properties.putAll( metadataFacet.toProperties() );
358
359         try
360         {
361             File directory = new File( getMetadataDirectory( repositoryId, metadataFacet.getFacetId() ),
362                                        metadataFacet.getName() );
363             writeProperties( properties, directory, METADATA_KEY );
364         }
365         catch ( IOException e )
366         {
367             // TODO!
368             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
369         }
370     }
371
372     public void removeMetadataFacets( String repositoryId, String facetId )
373     {
374         try
375         {
376             FileUtils.deleteDirectory( getMetadataDirectory( repositoryId, facetId ) );
377         }
378         catch ( IOException e )
379         {
380             // TODO!
381             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
382         }
383     }
384
385     public void removeMetadataFacet( String repoId, String facetId, String name )
386     {
387         File dir = new File( getMetadataDirectory( repoId, facetId ), name );
388         try
389         {
390             FileUtils.deleteDirectory( dir );
391         }
392         catch ( IOException e )
393         {
394             // TODO
395             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
396         }
397     }
398
399     public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
400     {
401         // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
402         //  of this information (eg. in Lucene, as before)
403
404         List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
405         for ( String ns : getRootNamespaces( repoId ) )
406         {
407             getArtifactsByDateRange( artifacts, repoId, ns, startTime, endTime );
408         }
409         Collections.sort( artifacts, new ArtifactComparator() );
410         return artifacts;
411     }
412
413     private void getArtifactsByDateRange( List<ArtifactMetadata> artifacts, String repoId, String ns, Date startTime,
414                                           Date endTime )
415     {
416         for ( String namespace : getNamespaces( repoId, ns ) )
417         {
418             getArtifactsByDateRange( artifacts, repoId, ns + "." + namespace, startTime, endTime );
419         }
420
421         for ( String project : getProjects( repoId, ns ) )
422         {
423             for ( String version : getProjectVersions( repoId, ns, project ) )
424             {
425                 for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) )
426                 {
427                     if ( startTime == null || startTime.before( artifact.getWhenGathered() ) )
428                     {
429                         if ( endTime == null || endTime.after( artifact.getWhenGathered() ) )
430                         {
431                             artifacts.add( artifact );
432                         }
433                     }
434                 }
435             }
436         }
437     }
438
439     public Collection<ArtifactMetadata> getArtifacts( String repoId, String namespace, String projectId,
440                                                       String projectVersion )
441     {
442         Map<String, ArtifactMetadata> artifacts = new HashMap<String, ArtifactMetadata>();
443
444         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
445
446         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
447
448         for ( Map.Entry entry : properties.entrySet() )
449         {
450             String name = (String) entry.getKey();
451             StringTokenizer tok = new StringTokenizer( name, ":" );
452             if ( tok.hasMoreTokens() && "artifact".equals( tok.nextToken() ) )
453             {
454                 String field = tok.nextToken();
455                 String id = tok.nextToken();
456
457                 ArtifactMetadata artifact = artifacts.get( id );
458                 if ( artifact == null )
459                 {
460                     artifact = new ArtifactMetadata();
461                     artifact.setRepositoryId( repoId );
462                     artifact.setNamespace( namespace );
463                     artifact.setProject( projectId );
464                     artifact.setProjectVersion( projectVersion );
465                     artifact.setVersion( projectVersion );
466                     artifact.setId( id );
467                     artifacts.put( id, artifact );
468                 }
469
470                 String value = (String) entry.getValue();
471                 if ( "updated".equals( field ) )
472                 {
473                     artifact.setFileLastModified( Long.parseLong( value ) );
474                 }
475                 else if ( "size".equals( field ) )
476                 {
477                     artifact.setSize( Long.valueOf( value ) );
478                 }
479                 else if ( "whenGathered".equals( field ) )
480                 {
481                     artifact.setWhenGathered( new Date( Long.parseLong( value ) ) );
482                 }
483                 else if ( "version".equals( field ) )
484                 {
485                     artifact.setVersion( value );
486                 }
487                 else if ( "md5".equals( field ) )
488                 {
489                     artifact.setMd5( value );
490                 }
491                 else if ( "sha1".equals( field ) )
492                 {
493                     artifact.setSha1( value );
494                 }
495                 else if ( "facetIds".equals( field ) )
496                 {
497                     if ( value.length() > 0 )
498                     {
499                         String propertyPrefix = "artifact:facet:" + id + ":";
500                         for ( String facetId : value.split( "," ) )
501                         {
502                             MetadataFacetFactory factory = metadataFacetFactories.get( facetId );
503                             if ( factory == null )
504                             {
505                                 log.error( "Attempted to load unknown artifact metadata facet: " + facetId );
506                             }
507                             else
508                             {
509                                 MetadataFacet facet = factory.createMetadataFacet();
510                                 String prefix = propertyPrefix + facet.getFacetId();
511                                 Map<String, String> map = new HashMap<String, String>();
512                                 for ( Object key : new ArrayList( properties.keySet() ) )
513                                 {
514                                     String property = (String) key;
515                                     if ( property.startsWith( prefix ) )
516                                     {
517                                         map.put( property.substring( prefix.length() + 1 ), properties.getProperty(
518                                             property ) );
519                                     }
520                                 }
521                                 facet.fromProperties( map );
522                                 artifact.addFacet( facet );
523                             }
524                         }
525                     }
526
527                     updateArtifactFacets( artifact, properties );
528                 }
529             }
530         }
531         return artifacts.values();
532     }
533
534     public void save()
535         throws MetadataRepositoryException
536     {
537         // it's all instantly persisted
538     }
539
540     public void close()
541     {
542         // nothing additional to close
543     }
544
545     public void revert()
546         throws MetadataRepositoryException
547     {
548         log.warn( "Attempted to revert a session, but the file-based repository storage doesn't support it" );
549     }
550
551     private void updateArtifactFacets( ArtifactMetadata artifact, Properties properties )
552     {
553         String propertyPrefix = "artifact:facet:" + artifact.getId() + ":";
554         for ( MetadataFacet facet : artifact.getFacetList() )
555         {
556             for ( Map.Entry<String, String> e : facet.toProperties().entrySet() )
557             {
558                 String key = propertyPrefix + facet.getFacetId() + ":" + e.getKey();
559                 properties.setProperty( key, e.getValue() );
560             }
561         }
562     }
563
564     public Collection<String> getRepositories()
565     {
566         return configuration.getConfiguration().getManagedRepositoriesAsMap().keySet();
567     }
568
569     public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
570     {
571         // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
572         //  of this information (eg. in Lucene, as before)
573         // alternatively, we could build a referential tree in the content repository, however it would need some levels
574         // of depth to avoid being too broad to be useful (eg. /repository/checksums/a/ab/abcdef1234567)
575
576         List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
577         for ( String ns : getRootNamespaces( repositoryId ) )
578         {
579             getArtifactsByChecksum( artifacts, repositoryId, ns, checksum );
580         }
581         return artifacts;
582     }
583
584     public void removeArtifact( String repoId, String namespace, String project, String version, String id )
585     {
586         File directory = new File( getDirectory( repoId ), namespace + "/" + project + "/" + version );
587
588         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
589
590         properties.remove( "artifact:updated:" + id );
591         properties.remove( "artifact:whenGathered:" + id );
592         properties.remove( "artifact:size:" + id );
593         properties.remove( "artifact:md5:" + id );
594         properties.remove( "artifact:sha1:" + id );
595         properties.remove( "artifact:version:" + id );
596         properties.remove( "artifact:facetIds:" + id );
597
598         String prefix = "artifact:facet:" + id + ":";
599         for ( Object key : new ArrayList<Object>( properties.keySet() ) )
600         {
601             String property = (String) key;
602             if ( property.startsWith( prefix ) )
603             {
604                 properties.remove( property );
605             }
606         }
607
608         try
609         {
610             writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
611         }
612         catch ( IOException e )
613         {
614             // TODO
615             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
616         }
617     }
618
619     public void removeRepository( String repoId )
620     {
621         try
622         {
623             FileUtils.deleteDirectory( getDirectory( repoId ) );
624         }
625         catch ( IOException e )
626         {
627             // TODO
628             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
629         }
630     }
631
632     private void getArtifactsByChecksum( List<ArtifactMetadata> artifacts, String repositoryId, String ns,
633                                          String checksum )
634     {
635         for ( String namespace : getNamespaces( repositoryId, ns ) )
636         {
637             getArtifactsByChecksum( artifacts, repositoryId, ns + "." + namespace, checksum );
638         }
639
640         for ( String project : getProjects( repositoryId, ns ) )
641         {
642             for ( String version : getProjectVersions( repositoryId, ns, project ) )
643             {
644                 for ( ArtifactMetadata artifact : getArtifacts( repositoryId, ns, project, version ) )
645                 {
646                     if ( checksum.equals( artifact.getMd5() ) || checksum.equals( artifact.getSha1() ) )
647                     {
648                         artifacts.add( artifact );
649                     }
650                 }
651             }
652         }
653     }
654
655     private File getMetadataDirectory( String repoId, String facetId )
656     {
657         return new File( getBaseDirectory( repoId ), "facets/" + facetId );
658     }
659
660     private String join( Collection<String> ids )
661     {
662         if ( ids != null && !ids.isEmpty() )
663         {
664             StringBuilder s = new StringBuilder();
665             for ( String id : ids )
666             {
667                 s.append( id );
668                 s.append( "," );
669             }
670             return s.substring( 0, s.length() - 1 );
671         }
672         return "";
673     }
674
675     private void setProperty( Properties properties, String name, String value )
676     {
677         if ( value != null )
678         {
679             properties.setProperty( name, value );
680         }
681     }
682
683     public void updateArtifact( String repoId, String namespace, String projectId, String projectVersion,
684                                 ArtifactMetadata artifact )
685     {
686         ProjectVersionMetadata metadata = new ProjectVersionMetadata();
687         metadata.setId( projectVersion );
688         updateProjectVersion( repoId, namespace, projectId, metadata );
689
690         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
691
692         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
693
694         clearMetadataFacetProperties( artifact.getFacetList(), properties, "artifact:facet:" + artifact.getId() + ":" );
695
696         String id = artifact.getId();
697         properties.setProperty( "artifact:updated:" + id, Long.toString( artifact.getFileLastModified().getTime() ) );
698         properties.setProperty( "artifact:whenGathered:" + id, Long.toString( artifact.getWhenGathered().getTime() ) );
699         properties.setProperty( "artifact:size:" + id, Long.toString( artifact.getSize() ) );
700         if ( artifact.getMd5() != null )
701         {
702             properties.setProperty( "artifact:md5:" + id, artifact.getMd5() );
703         }
704         if ( artifact.getSha1() != null )
705         {
706             properties.setProperty( "artifact:sha1:" + id, artifact.getSha1() );
707         }
708         properties.setProperty( "artifact:version:" + id, artifact.getVersion() );
709
710         Set<String> facetIds = new LinkedHashSet<String>( artifact.getFacetIds() );
711         String property = "artifact:facetIds:" + id;
712         facetIds.addAll( Arrays.asList( properties.getProperty( property, "" ).split( "," ) ) );
713         properties.setProperty( property, join( facetIds ) );
714
715         updateArtifactFacets( artifact, properties );
716
717         try
718         {
719             writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
720         }
721         catch ( IOException e )
722         {
723             // TODO
724             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
725         }
726     }
727
728     private Properties readOrCreateProperties( File directory, String propertiesKey )
729     {
730         try
731         {
732             return readProperties( directory, propertiesKey );
733         }
734         catch ( FileNotFoundException e )
735         {
736             // ignore and return new properties
737         }
738         catch ( IOException e )
739         {
740             // TODO
741             e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
742         }
743         return new Properties();
744     }
745
746     private Properties readProperties( File directory, String propertiesKey )
747         throws IOException
748     {
749         Properties properties = new Properties();
750         FileInputStream in = null;
751         try
752         {
753             in = new FileInputStream( new File( directory, propertiesKey + ".properties" ) );
754             properties.load( in );
755         }
756         finally
757         {
758             IOUtils.closeQuietly( in );
759         }
760         return properties;
761     }
762
763     public ProjectMetadata getProject( String repoId, String namespace, String projectId )
764     {
765         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId );
766
767         Properties properties = readOrCreateProperties( directory, PROJECT_METADATA_KEY );
768
769         ProjectMetadata project = null;
770
771         String id = properties.getProperty( "id" );
772         if ( id != null )
773         {
774             project = new ProjectMetadata();
775             project.setNamespace( properties.getProperty( "namespace" ) );
776             project.setId( id );
777         }
778
779         return project;
780     }
781
782     public ProjectVersionMetadata getProjectVersion( String repoId, String namespace, String projectId,
783                                                      String projectVersion )
784     {
785         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
786
787         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
788         String id = properties.getProperty( "id" );
789         ProjectVersionMetadata versionMetadata = null;
790         if ( id != null )
791         {
792             versionMetadata = new ProjectVersionMetadata();
793             versionMetadata.setId( id );
794             versionMetadata.setName( properties.getProperty( "name" ) );
795             versionMetadata.setDescription( properties.getProperty( "description" ) );
796             versionMetadata.setUrl( properties.getProperty( "url" ) );
797             versionMetadata.setIncomplete( Boolean.valueOf( properties.getProperty( "incomplete", "false" ) ) );
798
799             String scmConnection = properties.getProperty( "scm.connection" );
800             String scmDeveloperConnection = properties.getProperty( "scm.developerConnection" );
801             String scmUrl = properties.getProperty( "scm.url" );
802             if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
803             {
804                 Scm scm = new Scm();
805                 scm.setConnection( scmConnection );
806                 scm.setDeveloperConnection( scmDeveloperConnection );
807                 scm.setUrl( scmUrl );
808                 versionMetadata.setScm( scm );
809             }
810
811             String ciSystem = properties.getProperty( "ci.system" );
812             String ciUrl = properties.getProperty( "ci.url" );
813             if ( ciSystem != null || ciUrl != null )
814             {
815                 CiManagement ci = new CiManagement();
816                 ci.setSystem( ciSystem );
817                 ci.setUrl( ciUrl );
818                 versionMetadata.setCiManagement( ci );
819             }
820
821             String issueSystem = properties.getProperty( "issue.system" );
822             String issueUrl = properties.getProperty( "issue.url" );
823             if ( issueSystem != null || issueUrl != null )
824             {
825                 IssueManagement issueManagement = new IssueManagement();
826                 issueManagement.setSystem( issueSystem );
827                 issueManagement.setUrl( issueUrl );
828                 versionMetadata.setIssueManagement( issueManagement );
829             }
830
831             String orgName = properties.getProperty( "org.name" );
832             String orgUrl = properties.getProperty( "org.url" );
833             if ( orgName != null || orgUrl != null )
834             {
835                 Organization org = new Organization();
836                 org.setName( orgName );
837                 org.setUrl( orgUrl );
838                 versionMetadata.setOrganization( org );
839             }
840
841             boolean done = false;
842             int i = 0;
843             while ( !done )
844             {
845                 String licenseName = properties.getProperty( "license." + i + ".name" );
846                 String licenseUrl = properties.getProperty( "license." + i + ".url" );
847                 if ( licenseName != null || licenseUrl != null )
848                 {
849                     License license = new License();
850                     license.setName( licenseName );
851                     license.setUrl( licenseUrl );
852                     versionMetadata.addLicense( license );
853                 }
854                 else
855                 {
856                     done = true;
857                 }
858                 i++;
859             }
860
861             done = false;
862             i = 0;
863             while ( !done )
864             {
865                 String mailingListName = properties.getProperty( "mailingList." + i + ".name" );
866                 if ( mailingListName != null )
867                 {
868                     MailingList mailingList = new MailingList();
869                     mailingList.setName( mailingListName );
870                     mailingList.setMainArchiveUrl( properties.getProperty( "mailingList." + i + ".archive" ) );
871                     String p = properties.getProperty( "mailingList." + i + ".otherArchives" );
872                     if ( p != null && p.length() > 0 )
873                     {
874                         mailingList.setOtherArchives( Arrays.asList( p.split( "," ) ) );
875                     }
876                     else
877                     {
878                         mailingList.setOtherArchives( Collections.<String>emptyList() );
879                     }
880                     mailingList.setPostAddress( properties.getProperty( "mailingList." + i + ".post" ) );
881                     mailingList.setSubscribeAddress( properties.getProperty( "mailingList." + i + ".subscribe" ) );
882                     mailingList.setUnsubscribeAddress( properties.getProperty( "mailingList." + i + ".unsubscribe" ) );
883                     versionMetadata.addMailingList( mailingList );
884                 }
885                 else
886                 {
887                     done = true;
888                 }
889                 i++;
890             }
891
892             done = false;
893             i = 0;
894             while ( !done )
895             {
896                 String dependencyArtifactId = properties.getProperty( "dependency." + i + ".artifactId" );
897                 if ( dependencyArtifactId != null )
898                 {
899                     Dependency dependency = new Dependency();
900                     dependency.setArtifactId( dependencyArtifactId );
901                     dependency.setGroupId( properties.getProperty( "dependency." + i + ".groupId" ) );
902                     dependency.setClassifier( properties.getProperty( "dependency." + i + ".classifier" ) );
903                     dependency.setOptional( Boolean.valueOf( properties.getProperty(
904                         "dependency." + i + ".optional" ) ) );
905                     dependency.setScope( properties.getProperty( "dependency." + i + ".scope" ) );
906                     dependency.setSystemPath( properties.getProperty( "dependency." + i + ".systemPath" ) );
907                     dependency.setType( properties.getProperty( "dependency." + i + ".type" ) );
908                     dependency.setVersion( properties.getProperty( "dependency." + i + ".version" ) );
909                     versionMetadata.addDependency( dependency );
910                 }
911                 else
912                 {
913                     done = true;
914                 }
915                 i++;
916             }
917
918             String facetIds = properties.getProperty( "facetIds", "" );
919             if ( facetIds.length() > 0 )
920             {
921                 for ( String facetId : facetIds.split( "," ) )
922                 {
923                     MetadataFacetFactory factory = metadataFacetFactories.get( facetId );
924                     if ( factory == null )
925                     {
926                         log.error( "Attempted to load unknown project version metadata facet: " + facetId );
927                     }
928                     else
929                     {
930                         MetadataFacet facet = factory.createMetadataFacet();
931                         Map<String, String> map = new HashMap<String, String>();
932                         for ( Object key : new ArrayList( properties.keySet() ) )
933                         {
934                             String property = (String) key;
935                             if ( property.startsWith( facet.getFacetId() ) )
936                             {
937                                 map.put( property.substring( facet.getFacetId().length() + 1 ), properties.getProperty(
938                                     property ) );
939                             }
940                         }
941                         facet.fromProperties( map );
942                         versionMetadata.addFacet( facet );
943                     }
944                 }
945             }
946
947             updateProjectVersionFacets( versionMetadata, properties );
948         }
949         return versionMetadata;
950     }
951
952     public Collection<String> getArtifactVersions( String repoId, String namespace, String projectId,
953                                                    String projectVersion )
954     {
955         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
956
957         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
958
959         Set<String> versions = new HashSet<String>();
960         for ( Map.Entry entry : properties.entrySet() )
961         {
962             String name = (String) entry.getKey();
963             if ( name.startsWith( "artifact:version:" ) )
964             {
965                 versions.add( (String) entry.getValue() );
966             }
967         }
968         return versions;
969     }
970
971     public Collection<ProjectVersionReference> getProjectReferences( String repoId, String namespace, String projectId,
972                                                                      String projectVersion )
973     {
974         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
975
976         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
977         int numberOfRefs = Integer.parseInt( properties.getProperty( "ref:lastReferenceNum", "-1" ) ) + 1;
978
979         List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
980         for ( int i = 0; i < numberOfRefs; i++ )
981         {
982             ProjectVersionReference reference = new ProjectVersionReference();
983             reference.setProjectId( properties.getProperty( "ref:reference." + i + ".projectId" ) );
984             reference.setNamespace( properties.getProperty( "ref:reference." + i + ".namespace" ) );
985             reference.setProjectVersion( properties.getProperty( "ref:reference." + i + ".projectVersion" ) );
986             reference.setReferenceType( ProjectVersionReference.ReferenceType.valueOf( properties.getProperty(
987                 "ref:reference." + i + ".referenceType" ) ) );
988             references.add( reference );
989         }
990         return references;
991     }
992
993     public Collection<String> getRootNamespaces( String repoId )
994     {
995         return getNamespaces( repoId, null );
996     }
997
998     public Collection<String> getNamespaces( String repoId, String baseNamespace )
999     {
1000         List<String> allNamespaces = new ArrayList<String>();
1001         File directory = getDirectory( repoId );
1002         File[] files = directory.listFiles();
1003         if ( files != null )
1004         {
1005             for ( File namespace : files )
1006             {
1007                 if ( new File( namespace, NAMESPACE_METADATA_KEY + ".properties" ).exists() )
1008                 {
1009                     allNamespaces.add( namespace.getName() );
1010                 }
1011             }
1012         }
1013
1014         Set<String> namespaces = new LinkedHashSet<String>();
1015         int fromIndex = baseNamespace != null ? baseNamespace.length() + 1 : 0;
1016         for ( String namespace : allNamespaces )
1017         {
1018             if ( baseNamespace == null || namespace.startsWith( baseNamespace + "." ) )
1019             {
1020                 int i = namespace.indexOf( '.', fromIndex );
1021                 if ( i >= 0 )
1022                 {
1023                     namespaces.add( namespace.substring( fromIndex, i ) );
1024                 }
1025                 else
1026                 {
1027                     namespaces.add( namespace.substring( fromIndex ) );
1028                 }
1029             }
1030         }
1031         return new ArrayList<String>( namespaces );
1032     }
1033
1034     public Collection<String> getProjects( String repoId, String namespace )
1035     {
1036         List<String> projects = new ArrayList<String>();
1037         File directory = new File( getDirectory( repoId ), namespace );
1038         File[] files = directory.listFiles();
1039         if ( files != null )
1040         {
1041             for ( File project : files )
1042             {
1043                 if ( new File( project, PROJECT_METADATA_KEY + ".properties" ).exists() )
1044                 {
1045                     projects.add( project.getName() );
1046                 }
1047             }
1048         }
1049         return projects;
1050     }
1051
1052     public Collection<String> getProjectVersions( String repoId, String namespace, String projectId )
1053     {
1054         List<String> projectVersions = new ArrayList<String>();
1055         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId );
1056         File[] files = directory.listFiles();
1057         if ( files != null )
1058         {
1059             for ( File projectVersion : files )
1060             {
1061                 if ( new File( projectVersion, PROJECT_VERSION_METADATA_KEY + ".properties" ).exists() )
1062                 {
1063                     projectVersions.add( projectVersion.getName() );
1064                 }
1065             }
1066         }
1067         return projectVersions;
1068     }
1069
1070     private void writeProperties( Properties properties, File directory, String propertiesKey )
1071         throws IOException
1072     {
1073         directory.mkdirs();
1074         FileOutputStream os = new FileOutputStream( new File( directory, propertiesKey + ".properties" ) );
1075         try
1076         {
1077             properties.store( os, null );
1078         }
1079         finally
1080         {
1081             IOUtils.closeQuietly( os );
1082         }
1083     }
1084
1085     private static class ArtifactComparator
1086         implements Comparator<ArtifactMetadata>
1087     {
1088         public int compare( ArtifactMetadata artifact1, ArtifactMetadata artifact2 )
1089         {
1090             if ( artifact1.getWhenGathered() == artifact2.getWhenGathered() )
1091             {
1092                 return 0;
1093             }
1094             if ( artifact1.getWhenGathered() == null )
1095             {
1096                 return 1;
1097             }
1098             if ( artifact2.getWhenGathered() == null )
1099             {
1100                 return -1;
1101             }
1102             return artifact1.getWhenGathered().compareTo( artifact2.getWhenGathered() );
1103         }
1104     }
1105
1106     public List<ArtifactMetadata> getArtifacts( String repoId )
1107     {
1108         List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
1109         for ( String ns : getRootNamespaces( repoId ) )
1110         {
1111             getArtifacts( artifacts, repoId, ns );
1112         }
1113         return artifacts;
1114     }
1115
1116     private void getArtifacts( List<ArtifactMetadata> artifacts, String repoId, String ns )
1117     {
1118         for ( String namespace : getNamespaces( repoId, ns ) )
1119         {
1120             getArtifacts( artifacts, repoId, ns + "." + namespace );
1121         }
1122
1123         for ( String project : getProjects( repoId, ns ) )
1124         {
1125             for ( String version : getProjectVersions( repoId, ns, project ) )
1126             {
1127                 for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) )
1128                 {
1129                     artifacts.add( artifact );
1130                 }
1131             }
1132         }
1133     }
1134 }