]> source.dussan.org Git - archiva.git/blob
0e62fe37598fc96f0dcbfd7fc92ed1db44dba513
[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     public boolean hasMetadataFacet( String repositoryId, String facetId )
313         throws MetadataRepositoryException
314     {
315         // TODO could be improved a bit
316         return !getMetadataFacets( repositoryId, facetId ).isEmpty();
317     }
318
319     private void recurse( List<String> facets, String prefix, File directory )
320     {
321         File[] list = directory.listFiles();
322         if ( list != null )
323         {
324             for ( File dir : list )
325             {
326                 if ( dir.isDirectory() )
327                 {
328                     recurse( facets, prefix + "/" + dir.getName(), dir );
329                 }
330                 else if ( dir.getName().equals( METADATA_KEY + ".properties" ) )
331                 {
332                     facets.add( prefix.substring( 1 ) );
333                 }
334             }
335         }
336     }
337
338     public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
339     {
340         Properties properties;
341         try
342         {
343             properties =
344                 readProperties( new File( getMetadataDirectory( repositoryId, facetId ), name ), METADATA_KEY );
345         }
346         catch ( FileNotFoundException e )
347         {
348             return null;
349         }
350         catch ( IOException e )
351         {
352             // TODO
353             log.error( e.getMessage(), e );
354             return null;
355         }
356         MetadataFacet metadataFacet = null;
357         MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
358         if ( metadataFacetFactory != null )
359         {
360             metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
361             Map<String, String> map = new HashMap<String, String>();
362             for ( Object key : new ArrayList( properties.keySet() ) )
363             {
364                 String property = (String) key;
365                 map.put( property, properties.getProperty( property ) );
366             }
367             metadataFacet.fromProperties( map );
368         }
369         return metadataFacet;
370     }
371
372     public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
373     {
374         Properties properties = new Properties();
375         properties.putAll( metadataFacet.toProperties() );
376
377         try
378         {
379             File directory =
380                 new File( getMetadataDirectory( repositoryId, metadataFacet.getFacetId() ), metadataFacet.getName() );
381             writeProperties( properties, directory, METADATA_KEY );
382         }
383         catch ( IOException e )
384         {
385             // TODO!
386             log.error( e.getMessage(), e );
387         }
388     }
389
390     public void removeMetadataFacets( String repositoryId, String facetId )
391     {
392         File dir = getMetadataDirectory( repositoryId, facetId );
393         if ( !FileUtils.deleteQuietly( dir ) )
394         {
395             log.error( "Cannot delete the metadata repository {}", dir );
396         }
397     }
398
399     public void removeMetadataFacet( String repoId, String facetId, String name )
400     {
401         File dir = new File( getMetadataDirectory( repoId, facetId ), name );
402         if ( !FileUtils.deleteQuietly( dir ) )
403         {
404             log.error( "Cannot delete the metadata repository {}", dir );
405         }
406     }
407
408     public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
409     {
410         // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
411         //  of this information (eg. in Lucene, as before)
412
413         List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
414         for ( String ns : getRootNamespaces( repoId ) )
415         {
416             getArtifactsByDateRange( artifacts, repoId, ns, startTime, endTime );
417         }
418         Collections.sort( artifacts, new ArtifactComparator() );
419         return artifacts;
420     }
421
422     private void getArtifactsByDateRange( List<ArtifactMetadata> artifacts, String repoId, String ns, Date startTime,
423                                           Date endTime )
424     {
425         for ( String namespace : getNamespaces( repoId, ns ) )
426         {
427             getArtifactsByDateRange( artifacts, repoId, ns + "." + namespace, startTime, endTime );
428         }
429
430         for ( String project : getProjects( repoId, ns ) )
431         {
432             for ( String version : getProjectVersions( repoId, ns, project ) )
433             {
434                 for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) )
435                 {
436                     if ( startTime == null || startTime.before( artifact.getWhenGathered() ) )
437                     {
438                         if ( endTime == null || endTime.after( artifact.getWhenGathered() ) )
439                         {
440                             artifacts.add( artifact );
441                         }
442                     }
443                 }
444             }
445         }
446     }
447
448     public Collection<ArtifactMetadata> getArtifacts( String repoId, String namespace, String projectId,
449                                                       String projectVersion )
450     {
451         Map<String, ArtifactMetadata> artifacts = new HashMap<String, ArtifactMetadata>();
452
453         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
454
455         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
456
457         for ( Map.Entry entry : properties.entrySet() )
458         {
459             String name = (String) entry.getKey();
460             StringTokenizer tok = new StringTokenizer( name, ":" );
461             if ( tok.hasMoreTokens() && "artifact".equals( tok.nextToken() ) )
462             {
463                 String field = tok.nextToken();
464                 String id = tok.nextToken();
465
466                 ArtifactMetadata artifact = artifacts.get( id );
467                 if ( artifact == null )
468                 {
469                     artifact = new ArtifactMetadata();
470                     artifact.setRepositoryId( repoId );
471                     artifact.setNamespace( namespace );
472                     artifact.setProject( projectId );
473                     artifact.setProjectVersion( projectVersion );
474                     artifact.setVersion( projectVersion );
475                     artifact.setId( id );
476                     artifacts.put( id, artifact );
477                 }
478
479                 String value = (String) entry.getValue();
480                 if ( "updated".equals( field ) )
481                 {
482                     artifact.setFileLastModified( Long.parseLong( value ) );
483                 }
484                 else if ( "size".equals( field ) )
485                 {
486                     artifact.setSize( Long.valueOf( value ) );
487                 }
488                 else if ( "whenGathered".equals( field ) )
489                 {
490                     artifact.setWhenGathered( new Date( Long.parseLong( value ) ) );
491                 }
492                 else if ( "version".equals( field ) )
493                 {
494                     artifact.setVersion( value );
495                 }
496                 else if ( "md5".equals( field ) )
497                 {
498                     artifact.setMd5( value );
499                 }
500                 else if ( "sha1".equals( field ) )
501                 {
502                     artifact.setSha1( value );
503                 }
504                 else if ( "facetIds".equals( field ) )
505                 {
506                     if ( value.length() > 0 )
507                     {
508                         String propertyPrefix = "artifact:facet:" + id + ":";
509                         for ( String facetId : value.split( "," ) )
510                         {
511                             MetadataFacetFactory factory = metadataFacetFactories.get( facetId );
512                             if ( factory == null )
513                             {
514                                 log.error( "Attempted to load unknown artifact metadata facet: " + facetId );
515                             }
516                             else
517                             {
518                                 MetadataFacet facet = factory.createMetadataFacet();
519                                 String prefix = propertyPrefix + facet.getFacetId();
520                                 Map<String, String> map = new HashMap<String, String>();
521                                 for ( Object key : new ArrayList( properties.keySet() ) )
522                                 {
523                                     String property = (String) key;
524                                     if ( property.startsWith( prefix ) )
525                                     {
526                                         map.put( property.substring( prefix.length() + 1 ),
527                                                  properties.getProperty( property ) );
528                                     }
529                                 }
530                                 facet.fromProperties( map );
531                                 artifact.addFacet( facet );
532                             }
533                         }
534                     }
535
536                     updateArtifactFacets( artifact, properties );
537                 }
538             }
539         }
540         return artifacts.values();
541     }
542
543     public void save()
544     {
545         // it's all instantly persisted
546     }
547
548     public void close()
549     {
550         // nothing additional to close
551     }
552
553     public void revert()
554     {
555         log.warn( "Attempted to revert a session, but the file-based repository storage doesn't support it" );
556     }
557
558     public boolean canObtainAccess( Class<?> aClass )
559     {
560         return false;
561     }
562
563     public Object obtainAccess( Class<?> aClass )
564     {
565         throw new IllegalArgumentException(
566             "Access using " + aClass + " is not supported on the file metadata storage" );
567     }
568
569     private void updateArtifactFacets( ArtifactMetadata artifact, Properties properties )
570     {
571         String propertyPrefix = "artifact:facet:" + artifact.getId() + ":";
572         for ( MetadataFacet facet : artifact.getFacetList() )
573         {
574             for ( Map.Entry<String, String> e : facet.toProperties().entrySet() )
575             {
576                 String key = propertyPrefix + facet.getFacetId() + ":" + e.getKey();
577                 properties.setProperty( key, e.getValue() );
578             }
579         }
580     }
581
582     public Collection<String> getRepositories()
583     {
584         List<String> repositories = new ArrayList<String>();
585         for ( ManagedRepositoryConfiguration managedRepositoryConfiguration : configuration.getConfiguration().getManagedRepositories() )
586         {
587             repositories.add( managedRepositoryConfiguration.getId() );
588         }
589         return repositories;
590     }
591
592     public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
593     {
594         // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
595         //  of this information (eg. in Lucene, as before)
596         // alternatively, we could build a referential tree in the content repository, however it would need some levels
597         // of depth to avoid being too broad to be useful (eg. /repository/checksums/a/ab/abcdef1234567)
598
599         List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
600         for ( String ns : getRootNamespaces( repositoryId ) )
601         {
602             getArtifactsByChecksum( artifacts, repositoryId, ns, checksum );
603         }
604         return artifacts;
605     }
606
607     public void removeNamespace( String repositoryId, String project )
608         throws MetadataRepositoryException
609     {
610         try
611         {
612             File namespaceDirectory = new File( getDirectory( repositoryId ), project );
613             FileUtils.deleteDirectory( namespaceDirectory );
614             //Properties properties = new Properties();
615             //properties.setProperty( "namespace", namespace );
616             //writeProperties( properties, namespaceDirectory, NAMESPACE_METADATA_KEY );
617
618         }
619         catch ( IOException e )
620         {
621             throw new MetadataRepositoryException( e.getMessage(), e );
622         }
623     }
624
625     public void removeArtifact( ArtifactMetadata artifactMetadata, String baseVersion )
626         throws MetadataRepositoryException
627     {
628
629         File directory = new File( getDirectory( artifactMetadata.getRepositoryId() ),
630                                    artifactMetadata.getNamespace() + "/" + artifactMetadata.getProject() + "/"
631                                        + baseVersion );
632
633         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
634
635         String id = artifactMetadata.getId();
636
637         properties.remove( "artifact:updated:" + id );
638         properties.remove( "artifact:whenGathered:" + id );
639         properties.remove( "artifact:size:" + id );
640         properties.remove( "artifact:md5:" + id );
641         properties.remove( "artifact:sha1:" + id );
642         properties.remove( "artifact:version:" + id );
643         properties.remove( "artifact:facetIds:" + id );
644
645         String prefix = "artifact:facet:" + id + ":";
646         for ( Object key : new ArrayList<Object>( properties.keySet() ) )
647         {
648             String property = (String) key;
649             if ( property.startsWith( prefix ) )
650             {
651                 properties.remove( property );
652             }
653         }
654
655         try
656         {
657             writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
658         }
659         catch ( IOException e )
660         {
661             // TODO
662             log.error( e.getMessage(), e );
663         }
664
665     }
666
667     public void removeArtifact( String repoId, String namespace, String project, String version, String id )
668     {
669
670         File directory = new File( getDirectory( repoId ), namespace + "/" + project + "/" + version );
671
672         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
673
674         properties.remove( "artifact:updated:" + id );
675         properties.remove( "artifact:whenGathered:" + id );
676         properties.remove( "artifact:size:" + id );
677         properties.remove( "artifact:md5:" + id );
678         properties.remove( "artifact:sha1:" + id );
679         properties.remove( "artifact:version:" + id );
680         properties.remove( "artifact:facetIds:" + id );
681
682         String prefix = "artifact:facet:" + id + ":";
683         for ( Object key : new ArrayList<Object>( properties.keySet() ) )
684         {
685             String property = (String) key;
686             if ( property.startsWith( prefix ) )
687             {
688                 properties.remove( property );
689             }
690         }
691
692         try
693         {
694
695             FileUtils.deleteDirectory( directory );
696
697             //writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
698         }
699         catch ( IOException e )
700         {
701             // TODO
702             log.error( e.getMessage(), e );
703         }
704     }
705
706     /**
707      * FIXME implements this !!!!
708      *
709      * @param repositoryId
710      * @param namespace
711      * @param project
712      * @param projectVersion
713      * @param metadataFacet  will remove artifacts which have this {@link MetadataFacet} using equals
714      * @throws MetadataRepositoryException
715      */
716     public void removeArtifact( String repositoryId, String namespace, String project, String projectVersion,
717                                 MetadataFacet metadataFacet )
718         throws MetadataRepositoryException
719     {
720         throw new UnsupportedOperationException( "not implemented" );
721     }
722
723     public void removeRepository( String repoId )
724     {
725         File dir = getDirectory( repoId );
726         if ( !FileUtils.deleteQuietly( dir ) )
727         {
728             log.error( "Cannot delete repository {}", dir );
729         }
730     }
731
732     private void getArtifactsByChecksum( List<ArtifactMetadata> artifacts, String repositoryId, String ns,
733                                          String checksum )
734     {
735         for ( String namespace : getNamespaces( repositoryId, ns ) )
736         {
737             getArtifactsByChecksum( artifacts, repositoryId, ns + "." + namespace, checksum );
738         }
739
740         for ( String project : getProjects( repositoryId, ns ) )
741         {
742             for ( String version : getProjectVersions( repositoryId, ns, project ) )
743             {
744                 for ( ArtifactMetadata artifact : getArtifacts( repositoryId, ns, project, version ) )
745                 {
746                     if ( checksum.equals( artifact.getMd5() ) || checksum.equals( artifact.getSha1() ) )
747                     {
748                         artifacts.add( artifact );
749                     }
750                 }
751             }
752         }
753     }
754
755     private File getMetadataDirectory( String repoId, String facetId )
756     {
757         return new File( getBaseDirectory( repoId ), "facets/" + facetId );
758     }
759
760     private String join( Collection<String> ids )
761     {
762         if ( ids != null && !ids.isEmpty() )
763         {
764             StringBuilder s = new StringBuilder();
765             for ( String id : ids )
766             {
767                 s.append( id );
768                 s.append( "," );
769             }
770             return s.substring( 0, s.length() - 1 );
771         }
772         return "";
773     }
774
775     private void setProperty( Properties properties, String name, String value )
776     {
777         if ( value != null )
778         {
779             properties.setProperty( name, value );
780         }
781     }
782
783     public void updateArtifact( String repoId, String namespace, String projectId, String projectVersion,
784                                 ArtifactMetadata artifact )
785     {
786         ProjectVersionMetadata metadata = new ProjectVersionMetadata();
787         metadata.setId( projectVersion );
788         updateProjectVersion( repoId, namespace, projectId, metadata );
789
790         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
791
792         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
793
794         clearMetadataFacetProperties( artifact.getFacetList(), properties, "artifact:facet:" + artifact.getId() + ":" );
795
796         String id = artifact.getId();
797         properties.setProperty( "artifact:updated:" + id, Long.toString( artifact.getFileLastModified().getTime() ) );
798         properties.setProperty( "artifact:whenGathered:" + id, Long.toString( artifact.getWhenGathered().getTime() ) );
799         properties.setProperty( "artifact:size:" + id, Long.toString( artifact.getSize() ) );
800         if ( artifact.getMd5() != null )
801         {
802             properties.setProperty( "artifact:md5:" + id, artifact.getMd5() );
803         }
804         if ( artifact.getSha1() != null )
805         {
806             properties.setProperty( "artifact:sha1:" + id, artifact.getSha1() );
807         }
808         properties.setProperty( "artifact:version:" + id, artifact.getVersion() );
809
810         Set<String> facetIds = new LinkedHashSet<String>( artifact.getFacetIds() );
811         String property = "artifact:facetIds:" + id;
812         facetIds.addAll( Arrays.asList( properties.getProperty( property, "" ).split( "," ) ) );
813         properties.setProperty( property, join( facetIds ) );
814
815         updateArtifactFacets( artifact, properties );
816
817         try
818         {
819             writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
820         }
821         catch ( IOException e )
822         {
823             // TODO
824             log.error( e.getMessage(), e );
825         }
826     }
827
828     private Properties readOrCreateProperties( File directory, String propertiesKey )
829     {
830         try
831         {
832             return readProperties( directory, propertiesKey );
833         }
834         catch ( FileNotFoundException e )
835         {
836             // ignore and return new properties
837         }
838         catch ( IOException e )
839         {
840             // TODO
841             log.error( e.getMessage(), e );
842         }
843         return new Properties();
844     }
845
846     private Properties readProperties( File directory, String propertiesKey )
847         throws IOException
848     {
849         Properties properties = new Properties();
850         FileInputStream in = null;
851         try
852         {
853             in = new FileInputStream( new File( directory, propertiesKey + ".properties" ) );
854             properties.load( in );
855         }
856         finally
857         {
858             IOUtils.closeQuietly( in );
859         }
860         return properties;
861     }
862
863     public ProjectMetadata getProject( String repoId, String namespace, String projectId )
864     {
865         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId );
866
867         Properties properties = readOrCreateProperties( directory, PROJECT_METADATA_KEY );
868
869         ProjectMetadata project = null;
870
871         String id = properties.getProperty( "id" );
872         if ( id != null )
873         {
874             project = new ProjectMetadata();
875             project.setNamespace( properties.getProperty( "namespace" ) );
876             project.setId( id );
877         }
878
879         return project;
880     }
881
882     public ProjectVersionMetadata getProjectVersion( String repoId, String namespace, String projectId,
883                                                      String projectVersion )
884     {
885         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
886
887         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
888         String id = properties.getProperty( "id" );
889         ProjectVersionMetadata versionMetadata = null;
890         if ( id != null )
891         {
892             versionMetadata = new ProjectVersionMetadata();
893             versionMetadata.setId( id );
894             versionMetadata.setName( properties.getProperty( "name" ) );
895             versionMetadata.setDescription( properties.getProperty( "description" ) );
896             versionMetadata.setUrl( properties.getProperty( "url" ) );
897             versionMetadata.setIncomplete( Boolean.valueOf( properties.getProperty( "incomplete", "false" ) ) );
898
899             String scmConnection = properties.getProperty( "scm.connection" );
900             String scmDeveloperConnection = properties.getProperty( "scm.developerConnection" );
901             String scmUrl = properties.getProperty( "scm.url" );
902             if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
903             {
904                 Scm scm = new Scm();
905                 scm.setConnection( scmConnection );
906                 scm.setDeveloperConnection( scmDeveloperConnection );
907                 scm.setUrl( scmUrl );
908                 versionMetadata.setScm( scm );
909             }
910
911             String ciSystem = properties.getProperty( "ci.system" );
912             String ciUrl = properties.getProperty( "ci.url" );
913             if ( ciSystem != null || ciUrl != null )
914             {
915                 CiManagement ci = new CiManagement();
916                 ci.setSystem( ciSystem );
917                 ci.setUrl( ciUrl );
918                 versionMetadata.setCiManagement( ci );
919             }
920
921             String issueSystem = properties.getProperty( "issue.system" );
922             String issueUrl = properties.getProperty( "issue.url" );
923             if ( issueSystem != null || issueUrl != null )
924             {
925                 IssueManagement issueManagement = new IssueManagement();
926                 issueManagement.setSystem( issueSystem );
927                 issueManagement.setUrl( issueUrl );
928                 versionMetadata.setIssueManagement( issueManagement );
929             }
930
931             String orgName = properties.getProperty( "org.name" );
932             String orgUrl = properties.getProperty( "org.url" );
933             if ( orgName != null || orgUrl != null )
934             {
935                 Organization org = new Organization();
936                 org.setName( orgName );
937                 org.setUrl( orgUrl );
938                 versionMetadata.setOrganization( org );
939             }
940
941             boolean done = false;
942             int i = 0;
943             while ( !done )
944             {
945                 String licenseName = properties.getProperty( "license." + i + ".name" );
946                 String licenseUrl = properties.getProperty( "license." + i + ".url" );
947                 if ( licenseName != null || licenseUrl != null )
948                 {
949                     License license = new License();
950                     license.setName( licenseName );
951                     license.setUrl( licenseUrl );
952                     versionMetadata.addLicense( license );
953                 }
954                 else
955                 {
956                     done = true;
957                 }
958                 i++;
959             }
960
961             done = false;
962             i = 0;
963             while ( !done )
964             {
965                 String mailingListName = properties.getProperty( "mailingList." + i + ".name" );
966                 if ( mailingListName != null )
967                 {
968                     MailingList mailingList = new MailingList();
969                     mailingList.setName( mailingListName );
970                     mailingList.setMainArchiveUrl( properties.getProperty( "mailingList." + i + ".archive" ) );
971                     String p = properties.getProperty( "mailingList." + i + ".otherArchives" );
972                     if ( p != null && p.length() > 0 )
973                     {
974                         mailingList.setOtherArchives( Arrays.asList( p.split( "," ) ) );
975                     }
976                     else
977                     {
978                         mailingList.setOtherArchives( Collections.<String>emptyList() );
979                     }
980                     mailingList.setPostAddress( properties.getProperty( "mailingList." + i + ".post" ) );
981                     mailingList.setSubscribeAddress( properties.getProperty( "mailingList." + i + ".subscribe" ) );
982                     mailingList.setUnsubscribeAddress( properties.getProperty( "mailingList." + i + ".unsubscribe" ) );
983                     versionMetadata.addMailingList( mailingList );
984                 }
985                 else
986                 {
987                     done = true;
988                 }
989                 i++;
990             }
991
992             done = false;
993             i = 0;
994             while ( !done )
995             {
996                 String dependencyArtifactId = properties.getProperty( "dependency." + i + ".artifactId" );
997                 if ( dependencyArtifactId != null )
998                 {
999                     Dependency dependency = new Dependency();
1000                     dependency.setArtifactId( dependencyArtifactId );
1001                     dependency.setGroupId( properties.getProperty( "dependency." + i + ".groupId" ) );
1002                     dependency.setClassifier( properties.getProperty( "dependency." + i + ".classifier" ) );
1003                     dependency.setOptional(
1004                         Boolean.valueOf( properties.getProperty( "dependency." + i + ".optional" ) ) );
1005                     dependency.setScope( properties.getProperty( "dependency." + i + ".scope" ) );
1006                     dependency.setSystemPath( properties.getProperty( "dependency." + i + ".systemPath" ) );
1007                     dependency.setType( properties.getProperty( "dependency." + i + ".type" ) );
1008                     dependency.setVersion( properties.getProperty( "dependency." + i + ".version" ) );
1009                     dependency.setOptional(
1010                         Boolean.valueOf( properties.getProperty( "dependency." + i + ".optional" ) ) );
1011                     versionMetadata.addDependency( dependency );
1012                 }
1013                 else
1014                 {
1015                     done = true;
1016                 }
1017                 i++;
1018             }
1019
1020             String facetIds = properties.getProperty( "facetIds", "" );
1021             if ( facetIds.length() > 0 )
1022             {
1023                 for ( String facetId : facetIds.split( "," ) )
1024                 {
1025                     MetadataFacetFactory factory = metadataFacetFactories.get( facetId );
1026                     if ( factory == null )
1027                     {
1028                         log.error( "Attempted to load unknown project version metadata facet: {}", facetId );
1029                     }
1030                     else
1031                     {
1032                         MetadataFacet facet = factory.createMetadataFacet();
1033                         Map<String, String> map = new HashMap<String, String>();
1034                         for ( Object key : new ArrayList( properties.keySet() ) )
1035                         {
1036                             String property = (String) key;
1037                             if ( property.startsWith( facet.getFacetId() ) )
1038                             {
1039                                 map.put( property.substring( facet.getFacetId().length() + 1 ),
1040                                          properties.getProperty( property ) );
1041                             }
1042                         }
1043                         facet.fromProperties( map );
1044                         versionMetadata.addFacet( facet );
1045                     }
1046                 }
1047             }
1048
1049             updateProjectVersionFacets( versionMetadata, properties );
1050         }
1051         return versionMetadata;
1052     }
1053
1054     public Collection<String> getArtifactVersions( String repoId, String namespace, String projectId,
1055                                                    String projectVersion )
1056     {
1057         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
1058
1059         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
1060
1061         Set<String> versions = new HashSet<String>();
1062         for ( Map.Entry entry : properties.entrySet() )
1063         {
1064             String name = (String) entry.getKey();
1065             if ( name.startsWith( "artifact:version:" ) )
1066             {
1067                 versions.add( (String) entry.getValue() );
1068             }
1069         }
1070         return versions;
1071     }
1072
1073     public Collection<ProjectVersionReference> getProjectReferences( String repoId, String namespace, String projectId,
1074                                                                      String projectVersion )
1075     {
1076         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
1077
1078         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
1079         int numberOfRefs = Integer.parseInt( properties.getProperty( "ref:lastReferenceNum", "-1" ) ) + 1;
1080
1081         List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
1082         for ( int i = 0; i < numberOfRefs; i++ )
1083         {
1084             ProjectVersionReference reference = new ProjectVersionReference();
1085             reference.setProjectId( properties.getProperty( "ref:reference." + i + ".projectId" ) );
1086             reference.setNamespace( properties.getProperty( "ref:reference." + i + ".namespace" ) );
1087             reference.setProjectVersion( properties.getProperty( "ref:reference." + i + ".projectVersion" ) );
1088             reference.setReferenceType( ProjectVersionReference.ReferenceType.valueOf(
1089                 properties.getProperty( "ref:reference." + i + ".referenceType" ) ) );
1090             references.add( reference );
1091         }
1092         return references;
1093     }
1094
1095     public Collection<String> getRootNamespaces( String repoId )
1096     {
1097         return getNamespaces( repoId, null );
1098     }
1099
1100     public Collection<String> getNamespaces( String repoId, String baseNamespace )
1101     {
1102         List<String> allNamespaces = new ArrayList<String>();
1103         File directory = getDirectory( repoId );
1104         File[] files = directory.listFiles();
1105         if ( files != null )
1106         {
1107             for ( File namespace : files )
1108             {
1109                 if ( new File( namespace, NAMESPACE_METADATA_KEY + ".properties" ).exists() )
1110                 {
1111                     allNamespaces.add( namespace.getName() );
1112                 }
1113             }
1114         }
1115
1116         Set<String> namespaces = new LinkedHashSet<String>();
1117         int fromIndex = baseNamespace != null ? baseNamespace.length() + 1 : 0;
1118         for ( String namespace : allNamespaces )
1119         {
1120             if ( baseNamespace == null || namespace.startsWith( baseNamespace + "." ) )
1121             {
1122                 int i = namespace.indexOf( '.', fromIndex );
1123                 if ( i >= 0 )
1124                 {
1125                     namespaces.add( namespace.substring( fromIndex, i ) );
1126                 }
1127                 else
1128                 {
1129                     namespaces.add( namespace.substring( fromIndex ) );
1130                 }
1131             }
1132         }
1133         return new ArrayList<String>( namespaces );
1134     }
1135
1136     public Collection<String> getProjects( String repoId, String namespace )
1137     {
1138         List<String> projects = new ArrayList<String>();
1139         File directory = new File( getDirectory( repoId ), namespace );
1140         File[] files = directory.listFiles();
1141         if ( files != null )
1142         {
1143             for ( File project : files )
1144             {
1145                 if ( new File( project, PROJECT_METADATA_KEY + ".properties" ).exists() )
1146                 {
1147                     projects.add( project.getName() );
1148                 }
1149             }
1150         }
1151         return projects;
1152     }
1153
1154     public Collection<String> getProjectVersions( String repoId, String namespace, String projectId )
1155     {
1156         List<String> projectVersions = new ArrayList<String>();
1157         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId );
1158         File[] files = directory.listFiles();
1159         if ( files != null )
1160         {
1161             for ( File projectVersion : files )
1162             {
1163                 if ( new File( projectVersion, PROJECT_VERSION_METADATA_KEY + ".properties" ).exists() )
1164                 {
1165                     projectVersions.add( projectVersion.getName() );
1166                 }
1167             }
1168         }
1169         return projectVersions;
1170     }
1171
1172     public void removeProject( String repositoryId, String namespace, String projectId )
1173         throws MetadataRepositoryException
1174     {
1175         File directory = new File( getDirectory( repositoryId ), namespace + "/" + projectId );
1176         try
1177         {
1178             if ( directory.exists() )
1179             {
1180                 FileUtils.deleteDirectory( directory );
1181             }
1182         }
1183         catch ( IOException e )
1184         {
1185             throw new MetadataRepositoryException( e.getMessage(), e );
1186         }
1187     }
1188
1189     public void removeProjectVersion( String repoId, String namespace, String projectId, String projectVersion )
1190         throws MetadataRepositoryException
1191     {
1192         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
1193         if ( directory.exists() )
1194         {
1195             try
1196             {
1197                 FileUtils.deleteDirectory( directory );
1198             }
1199             catch ( IOException e )
1200             {
1201                 throw new MetadataRepositoryException( e.getMessage(), e );
1202             }
1203         }
1204     }
1205
1206     private void writeProperties( Properties properties, File directory, String propertiesKey )
1207         throws IOException
1208     {
1209         directory.mkdirs();
1210         FileOutputStream os = new FileOutputStream( new File( directory, propertiesKey + ".properties" ) );
1211         try
1212         {
1213             properties.store( os, null );
1214         }
1215         finally
1216         {
1217             IOUtils.closeQuietly( os );
1218         }
1219     }
1220
1221     private static class ArtifactComparator
1222         implements Comparator<ArtifactMetadata>
1223     {
1224         public int compare( ArtifactMetadata artifact1, ArtifactMetadata artifact2 )
1225         {
1226             if ( artifact1.getWhenGathered() == artifact2.getWhenGathered() )
1227             {
1228                 return 0;
1229             }
1230             if ( artifact1.getWhenGathered() == null )
1231             {
1232                 return 1;
1233             }
1234             if ( artifact2.getWhenGathered() == null )
1235             {
1236                 return -1;
1237             }
1238             return artifact1.getWhenGathered().compareTo( artifact2.getWhenGathered() );
1239         }
1240     }
1241
1242     public List<ArtifactMetadata> getArtifacts( String repoId )
1243     {
1244         List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
1245         for ( String ns : getRootNamespaces( repoId ) )
1246         {
1247             getArtifacts( artifacts, repoId, ns );
1248         }
1249         return artifacts;
1250     }
1251
1252     private void getArtifacts( List<ArtifactMetadata> artifacts, String repoId, String ns )
1253     {
1254         for ( String namespace : getNamespaces( repoId, ns ) )
1255         {
1256             getArtifacts( artifacts, repoId, ns + "." + namespace );
1257         }
1258
1259         for ( String project : getProjects( repoId, ns ) )
1260         {
1261             for ( String version : getProjectVersions( repoId, ns, project ) )
1262             {
1263                 for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) )
1264                 {
1265                     artifacts.add( artifact );
1266                 }
1267             }
1268         }
1269     }
1270 }