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