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