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