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