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