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