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