1 package org.apache.archiva.metadata.repository.file;
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
12 * http://www.apache.org/licenses/LICENSE-2.0
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
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;
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.nio.file.NoSuchFileException;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.Collection;
53 import java.util.Collections;
54 import java.util.Comparator;
55 import java.util.Date;
56 import java.util.HashMap;
57 import java.util.HashSet;
58 import java.util.LinkedHashSet;
59 import java.util.List;
61 import java.util.Properties;
63 import java.util.StringTokenizer;
65 //import org.apache.commons.io.IOUtils;
67 public class FileMetadataRepository
68 implements MetadataRepository
70 private final Map<String, MetadataFacetFactory> metadataFacetFactories;
72 private final ArchivaConfiguration configuration;
74 private Logger log = LoggerFactory.getLogger( FileMetadataRepository.class );
76 private static final String PROJECT_METADATA_KEY = "project-metadata";
78 private static final String PROJECT_VERSION_METADATA_KEY = "version-metadata";
80 private static final String NAMESPACE_METADATA_KEY = "namespace-metadata";
82 private static final String METADATA_KEY = "metadata";
84 public FileMetadataRepository( Map<String, MetadataFacetFactory> metadataFacetFactories,
85 ArchivaConfiguration configuration )
87 this.metadataFacetFactories = metadataFacetFactories;
88 this.configuration = configuration;
91 private File getBaseDirectory( String repoId )
93 // TODO: should be configurable, like the index
94 String basedir = configuration.getConfiguration().getManagedRepositoriesAsMap().get( repoId ).getLocation();
95 return new File( basedir, ".archiva" );
98 private File getDirectory( String repoId )
100 return new File( getBaseDirectory( repoId ), "content" );
104 public void updateProject( String repoId, ProjectMetadata project )
106 updateProject( repoId, project.getNamespace(), project.getId() );
109 private void updateProject( String repoId, String namespace, String id )
111 // TODO: this is a more braindead implementation than we would normally expect, for prototyping purposes
112 updateNamespace( repoId, namespace );
116 File namespaceDirectory = new File( getDirectory( repoId ), namespace );
117 Properties properties = new Properties();
118 properties.setProperty( "namespace", namespace );
119 properties.setProperty( "id", id );
120 writeProperties( properties, new File( namespaceDirectory, id ), PROJECT_METADATA_KEY );
122 catch ( IOException e )
125 log.error( e.getMessage(), e );
130 public void updateProjectVersion( String repoId, String namespace, String projectId,
131 ProjectVersionMetadata versionMetadata )
133 updateProject( repoId, namespace, projectId );
136 new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + versionMetadata.getId() );
138 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
139 // remove properties that are not references or artifacts
140 for ( Object key : new ArrayList( properties.keySet() ) )
142 String name = (String) key;
143 if ( !name.contains( ":" ) && !name.equals( "facetIds" ) )
145 properties.remove( name );
148 // clear the facet contents so old properties are no longer written
149 clearMetadataFacetProperties( versionMetadata.getFacetList(), properties, "" );
151 properties.setProperty( "id", versionMetadata.getId() );
152 setProperty( properties, "name", versionMetadata.getName() );
153 setProperty( properties, "description", versionMetadata.getDescription() );
154 setProperty( properties, "url", versionMetadata.getUrl() );
155 setProperty( properties, "incomplete", String.valueOf( versionMetadata.isIncomplete() ) );
156 if ( versionMetadata.getScm() != null )
158 setProperty( properties, "scm.connection", versionMetadata.getScm().getConnection() );
159 setProperty( properties, "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
160 setProperty( properties, "scm.url", versionMetadata.getScm().getUrl() );
162 if ( versionMetadata.getCiManagement() != null )
164 setProperty( properties, "ci.system", versionMetadata.getCiManagement().getSystem() );
165 setProperty( properties, "ci.url", versionMetadata.getCiManagement().getUrl() );
167 if ( versionMetadata.getIssueManagement() != null )
169 setProperty( properties, "issue.system", versionMetadata.getIssueManagement().getSystem() );
170 setProperty( properties, "issue.url", versionMetadata.getIssueManagement().getUrl() );
172 if ( versionMetadata.getOrganization() != null )
174 setProperty( properties, "org.name", versionMetadata.getOrganization().getName() );
175 setProperty( properties, "org.url", versionMetadata.getOrganization().getUrl() );
178 for ( License license : versionMetadata.getLicenses() )
180 setProperty( properties, "license." + i + ".name", license.getName() );
181 setProperty( properties, "license." + i + ".url", license.getUrl() );
185 for ( MailingList mailingList : versionMetadata.getMailingLists() )
187 setProperty( properties, "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
188 setProperty( properties, "mailingList." + i + ".name", mailingList.getName() );
189 setProperty( properties, "mailingList." + i + ".post", mailingList.getPostAddress() );
190 setProperty( properties, "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
191 setProperty( properties, "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
192 setProperty( properties, "mailingList." + i + ".otherArchives", join( mailingList.getOtherArchives() ) );
196 ProjectVersionReference reference = new ProjectVersionReference();
197 reference.setNamespace( namespace );
198 reference.setProjectId( projectId );
199 reference.setProjectVersion( versionMetadata.getId() );
200 reference.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
201 for ( Dependency dependency : versionMetadata.getDependencies() )
203 setProperty( properties, "dependency." + i + ".classifier", dependency.getClassifier() );
204 setProperty( properties, "dependency." + i + ".scope", dependency.getScope() );
205 setProperty( properties, "dependency." + i + ".systemPath", dependency.getSystemPath() );
206 setProperty( properties, "dependency." + i + ".artifactId", dependency.getArtifactId() );
207 setProperty( properties, "dependency." + i + ".groupId", dependency.getGroupId() );
208 setProperty( properties, "dependency." + i + ".version", dependency.getVersion() );
209 setProperty( properties, "dependency." + i + ".type", dependency.getType() );
210 setProperty( properties, "dependency." + i + ".optional", String.valueOf( dependency.isOptional() ) );
212 updateProjectReference( repoId, dependency.getGroupId(), dependency.getArtifactId(),
213 dependency.getVersion(), reference );
217 Set<String> facetIds = new LinkedHashSet<String>( versionMetadata.getFacetIds() );
218 facetIds.addAll( Arrays.asList( properties.getProperty( "facetIds", "" ).split( "," ) ) );
219 properties.setProperty( "facetIds", join( facetIds ) );
221 updateProjectVersionFacets( versionMetadata, properties );
225 writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
227 catch ( IOException e )
230 log.error( e.getMessage(), e );
234 private void updateProjectVersionFacets( ProjectVersionMetadata versionMetadata, Properties properties )
236 for ( MetadataFacet facet : versionMetadata.getFacetList() )
238 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
240 properties.setProperty( facet.getFacetId() + ":" + entry.getKey(), entry.getValue() );
245 private static void clearMetadataFacetProperties( Collection<MetadataFacet> facetList, Properties properties,
248 List<Object> propsToRemove = new ArrayList<>();
249 for ( MetadataFacet facet : facetList )
251 for ( Object key : new ArrayList( properties.keySet() ) )
253 String keyString = (String) key;
254 if ( keyString.startsWith( prefix + facet.getFacetId() + ":" ) )
256 propsToRemove.add( key );
261 for ( Object key : propsToRemove )
263 properties.remove( key );
267 private void updateProjectReference( String repoId, String namespace, String projectId, String projectVersion,
268 ProjectVersionReference reference )
270 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
272 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
273 int i = Integer.parseInt( properties.getProperty( "ref:lastReferenceNum", "-1" ) ) + 1;
274 setProperty( properties, "ref:lastReferenceNum", Integer.toString( i ) );
275 setProperty( properties, "ref:reference." + i + ".namespace", reference.getNamespace() );
276 setProperty( properties, "ref:reference." + i + ".projectId", reference.getProjectId() );
277 setProperty( properties, "ref:reference." + i + ".projectVersion", reference.getProjectVersion() );
278 setProperty( properties, "ref:reference." + i + ".referenceType", reference.getReferenceType().toString() );
282 writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
284 catch ( IOException e )
287 log.error( e.getMessage(), e );
292 public void updateNamespace( String repoId, String namespace )
296 File namespaceDirectory = new File( getDirectory( repoId ), namespace );
297 Properties properties = new Properties();
298 properties.setProperty( "namespace", namespace );
299 writeProperties( properties, namespaceDirectory, NAMESPACE_METADATA_KEY );
302 catch ( IOException e )
305 log.error( e.getMessage(), e );
310 public List<String> getMetadataFacets( String repoId, String facetId )
312 File directory = getMetadataDirectory( repoId, facetId );
313 List<String> facets = new ArrayList<>();
314 recurse( facets, "", directory );
319 public boolean hasMetadataFacet( String repositoryId, String facetId )
320 throws MetadataRepositoryException
322 // TODO could be improved a bit
323 return !getMetadataFacets( repositoryId, facetId ).isEmpty();
326 private void recurse( List<String> facets, String prefix, File directory )
328 File[] list = directory.listFiles();
331 for ( File dir : list )
333 if ( dir.isDirectory() )
335 recurse( facets, prefix + "/" + dir.getName(), dir );
337 else if ( dir.getName().equals( METADATA_KEY + ".properties" ) )
339 facets.add( prefix.substring( 1 ) );
346 public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
348 Properties properties;
352 readProperties( new File( getMetadataDirectory( repositoryId, facetId ), name ), METADATA_KEY );
354 catch ( FileNotFoundException e )
358 catch ( IOException e )
361 log.error( e.getMessage(), e );
364 MetadataFacet metadataFacet = null;
365 MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
366 if ( metadataFacetFactory != null )
368 metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
369 Map<String, String> map = new HashMap<>();
370 for ( Object key : new ArrayList( properties.keySet() ) )
372 String property = (String) key;
373 map.put( property, properties.getProperty( property ) );
375 metadataFacet.fromProperties( map );
377 return metadataFacet;
381 public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
383 Properties properties = new Properties();
384 properties.putAll( metadataFacet.toProperties() );
389 new File( getMetadataDirectory( repositoryId, metadataFacet.getFacetId() ), metadataFacet.getName() );
390 writeProperties( properties, directory, METADATA_KEY );
392 catch ( IOException e )
395 log.error( e.getMessage(), e );
400 public void removeMetadataFacets( String repositoryId, String facetId )
402 File dir = getMetadataDirectory( repositoryId, facetId );
403 if ( !FileUtils.deleteQuietly( dir ) )
405 log.error( "Cannot delete the metadata repository {}", dir );
410 public void removeMetadataFacet( String repoId, String facetId, String name )
412 File dir = new File( getMetadataDirectory( repoId, facetId ), name );
413 if ( !FileUtils.deleteQuietly( dir ) )
415 log.error( "Cannot delete the metadata repository {}", dir );
420 public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
422 // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
423 // of this information (eg. in Lucene, as before)
425 List<ArtifactMetadata> artifacts = new ArrayList<>();
426 for ( String ns : getRootNamespaces( repoId ) )
428 getArtifactsByDateRange( artifacts, repoId, ns, startTime, endTime );
430 Collections.sort( artifacts, new ArtifactComparator() );
434 private void getArtifactsByDateRange( List<ArtifactMetadata> artifacts, String repoId, String ns, Date startTime,
437 for ( String namespace : getNamespaces( repoId, ns ) )
439 getArtifactsByDateRange( artifacts, repoId, ns + "." + namespace, startTime, endTime );
442 for ( String project : getProjects( repoId, ns ) )
444 for ( String version : getProjectVersions( repoId, ns, project ) )
446 for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) )
448 if ( startTime == null || startTime.before( artifact.getWhenGathered() ) )
450 if ( endTime == null || endTime.after( artifact.getWhenGathered() ) )
452 artifacts.add( artifact );
461 public Collection<ArtifactMetadata> getArtifacts( String repoId, String namespace, String projectId,
462 String projectVersion )
464 Map<String, ArtifactMetadata> artifacts = new HashMap<>();
466 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
468 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
470 for ( Map.Entry entry : properties.entrySet() )
472 String name = (String) entry.getKey();
473 StringTokenizer tok = new StringTokenizer( name, ":" );
474 if ( tok.hasMoreTokens() && "artifact".equals( tok.nextToken() ) )
476 String field = tok.nextToken();
477 String id = tok.nextToken();
479 ArtifactMetadata artifact = artifacts.get( id );
480 if ( artifact == null )
482 artifact = new ArtifactMetadata();
483 artifact.setRepositoryId( repoId );
484 artifact.setNamespace( namespace );
485 artifact.setProject( projectId );
486 artifact.setProjectVersion( projectVersion );
487 artifact.setVersion( projectVersion );
488 artifact.setId( id );
489 artifacts.put( id, artifact );
492 String value = (String) entry.getValue();
493 if ( "updated".equals( field ) )
495 artifact.setFileLastModified( Long.parseLong( value ) );
497 else if ( "size".equals( field ) )
499 artifact.setSize( Long.valueOf( value ) );
501 else if ( "whenGathered".equals( field ) )
503 artifact.setWhenGathered( new Date( Long.parseLong( value ) ) );
505 else if ( "version".equals( field ) )
507 artifact.setVersion( value );
509 else if ( "md5".equals( field ) )
511 artifact.setMd5( value );
513 else if ( "sha1".equals( field ) )
515 artifact.setSha1( value );
517 else if ( "facetIds".equals( field ) )
519 if ( value.length() > 0 )
521 String propertyPrefix = "artifact:facet:" + id + ":";
522 for ( String facetId : value.split( "," ) )
524 MetadataFacetFactory factory = metadataFacetFactories.get( facetId );
525 if ( factory == null )
527 log.error( "Attempted to load unknown artifact metadata facet: " + facetId );
531 MetadataFacet facet = factory.createMetadataFacet();
532 String prefix = propertyPrefix + facet.getFacetId();
533 Map<String, String> map = new HashMap<>();
534 for ( Object key : new ArrayList( properties.keySet() ) )
536 String property = (String) key;
537 if ( property.startsWith( prefix ) )
539 map.put( property.substring( prefix.length() + 1 ),
540 properties.getProperty( property ) );
543 facet.fromProperties( map );
544 artifact.addFacet( facet );
549 updateArtifactFacets( artifact, properties );
553 return artifacts.values();
559 // it's all instantly persisted
565 // nothing additional to close
571 log.warn( "Attempted to revert a session, but the file-based repository storage doesn't support it" );
575 public boolean canObtainAccess( Class<?> aClass )
581 public <T> T obtainAccess( Class<T> aClass )
583 throw new IllegalArgumentException(
584 "Access using " + aClass + " is not supported on the file metadata storage" );
587 private void updateArtifactFacets( ArtifactMetadata artifact, Properties properties )
589 String propertyPrefix = "artifact:facet:" + artifact.getId() + ":";
590 for ( MetadataFacet facet : artifact.getFacetList() )
592 for ( Map.Entry<String, String> e : facet.toProperties().entrySet() )
594 String key = propertyPrefix + facet.getFacetId() + ":" + e.getKey();
595 properties.setProperty( key, e.getValue() );
601 public Collection<String> getRepositories()
603 List<String> repositories = new ArrayList<>();
604 for ( ManagedRepositoryConfiguration managedRepositoryConfiguration : configuration.getConfiguration().getManagedRepositories() )
606 repositories.add( managedRepositoryConfiguration.getId() );
612 public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
614 // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
615 // of this information (eg. in Lucene, as before)
616 // alternatively, we could build a referential tree in the content repository, however it would need some levels
617 // of depth to avoid being too broad to be useful (eg. /repository/checksums/a/ab/abcdef1234567)
619 List<ArtifactMetadata> artifacts = new ArrayList<>();
620 for ( String ns : getRootNamespaces( repositoryId ) )
622 getArtifactsByChecksum( artifacts, repositoryId, ns, checksum );
628 public void removeNamespace( String repositoryId, String project )
629 throws MetadataRepositoryException
633 File namespaceDirectory = new File( getDirectory( repositoryId ), project );
634 FileUtils.deleteDirectory( namespaceDirectory );
635 //Properties properties = new Properties();
636 //properties.setProperty( "namespace", namespace );
637 //writeProperties( properties, namespaceDirectory, NAMESPACE_METADATA_KEY );
640 catch ( IOException e )
642 throw new MetadataRepositoryException( e.getMessage(), e );
647 public void removeArtifact( ArtifactMetadata artifactMetadata, String baseVersion )
648 throws MetadataRepositoryException
651 File directory = new File( getDirectory( artifactMetadata.getRepositoryId() ),
652 artifactMetadata.getNamespace() + "/" + artifactMetadata.getProject() + "/"
656 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
658 String id = artifactMetadata.getId();
660 properties.remove( "artifact:updated:" + id );
661 properties.remove( "artifact:whenGathered:" + id );
662 properties.remove( "artifact:size:" + id );
663 properties.remove( "artifact:md5:" + id );
664 properties.remove( "artifact:sha1:" + id );
665 properties.remove( "artifact:version:" + id );
666 properties.remove( "artifact:facetIds:" + id );
668 String prefix = "artifact:facet:" + id + ":";
669 for ( Object key : new ArrayList( properties.keySet() ) )
671 String property = (String) key;
672 if ( property.startsWith( prefix ) )
674 properties.remove( property );
680 writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
682 catch ( IOException e )
685 log.error( e.getMessage(), e );
691 public void removeArtifact( String repoId, String namespace, String project, String version, String id )
694 File directory = new File( getDirectory( repoId ), namespace + "/" + project + "/" + version );
696 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
698 properties.remove( "artifact:updated:" + id );
699 properties.remove( "artifact:whenGathered:" + id );
700 properties.remove( "artifact:size:" + id );
701 properties.remove( "artifact:md5:" + id );
702 properties.remove( "artifact:sha1:" + id );
703 properties.remove( "artifact:version:" + id );
704 properties.remove( "artifact:facetIds:" + id );
706 String prefix = "artifact:facet:" + id + ":";
707 for ( Object key : new ArrayList( properties.keySet() ) )
709 String property = (String) key;
710 if ( property.startsWith( prefix ) )
712 properties.remove( property );
719 FileUtils.deleteDirectory( directory );
721 //writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
723 catch ( IOException e )
726 log.error( e.getMessage(), e );
731 * FIXME implements this !!!!
733 * @param repositoryId
736 * @param projectVersion
737 * @param metadataFacet will remove artifacts which have this {@link MetadataFacet} using equals
738 * @throws MetadataRepositoryException
741 public void removeArtifact( String repositoryId, String namespace, String project, String projectVersion,
742 MetadataFacet metadataFacet )
743 throws MetadataRepositoryException
745 throw new UnsupportedOperationException( "not implemented" );
749 public void removeRepository( String repoId )
751 File dir = getDirectory( repoId );
752 if ( !FileUtils.deleteQuietly( dir ) )
754 log.error( "Cannot delete repository {}", dir );
758 private void getArtifactsByChecksum( List<ArtifactMetadata> artifacts, String repositoryId, String ns,
761 for ( String namespace : getNamespaces( repositoryId, ns ) )
763 getArtifactsByChecksum( artifacts, repositoryId, ns + "." + namespace, checksum );
766 for ( String project : getProjects( repositoryId, ns ) )
768 for ( String version : getProjectVersions( repositoryId, ns, project ) )
770 for ( ArtifactMetadata artifact : getArtifacts( repositoryId, ns, project, version ) )
772 if ( checksum.equals( artifact.getMd5() ) || checksum.equals( artifact.getSha1() ) )
774 artifacts.add( artifact );
782 public List<ArtifactMetadata> getArtifactsByProjectVersionMetadata( String key, String value, String repositoryId )
783 throws MetadataRepositoryException
785 throw new UnsupportedOperationException( "not yet implemented in File backend" );
789 public List<ArtifactMetadata> getArtifactsByMetadata( String key, String value, String repositoryId )
790 throws MetadataRepositoryException
792 throw new UnsupportedOperationException( "not yet implemented in File backend" );
796 public List<ArtifactMetadata> getArtifactsByProperty( String key, String value, String repositoryId )
797 throws MetadataRepositoryException
799 throw new UnsupportedOperationException( "getArtifactsByProperty not yet implemented in File backend" );
802 private File getMetadataDirectory( String repoId, String facetId )
804 return new File( getBaseDirectory( repoId ), "facets/" + facetId );
807 private String join( Collection<String> ids )
809 if ( ids != null && !ids.isEmpty() )
811 StringBuilder s = new StringBuilder();
812 for ( String id : ids )
817 return s.substring( 0, s.length() - 1 );
822 private void setProperty( Properties properties, String name, String value )
826 properties.setProperty( name, value );
831 public void updateArtifact( String repoId, String namespace, String projectId, String projectVersion,
832 ArtifactMetadata artifact )
834 ProjectVersionMetadata metadata = new ProjectVersionMetadata();
835 metadata.setId( projectVersion );
836 updateProjectVersion( repoId, namespace, projectId, metadata );
838 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
840 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
842 clearMetadataFacetProperties( artifact.getFacetList(), properties, "artifact:facet:" + artifact.getId() + ":" );
844 String id = artifact.getId();
845 properties.setProperty( "artifact:updated:" + id, Long.toString( artifact.getFileLastModified().getTime() ) );
846 properties.setProperty( "artifact:whenGathered:" + id, Long.toString( artifact.getWhenGathered().getTime() ) );
847 properties.setProperty( "artifact:size:" + id, Long.toString( artifact.getSize() ) );
848 if ( artifact.getMd5() != null )
850 properties.setProperty( "artifact:md5:" + id, artifact.getMd5() );
852 if ( artifact.getSha1() != null )
854 properties.setProperty( "artifact:sha1:" + id, artifact.getSha1() );
856 properties.setProperty( "artifact:version:" + id, artifact.getVersion() );
858 Set<String> facetIds = new LinkedHashSet<String>( artifact.getFacetIds() );
859 String property = "artifact:facetIds:" + id;
860 facetIds.addAll( Arrays.asList( properties.getProperty( property, "" ).split( "," ) ) );
861 properties.setProperty( property, join( facetIds ) );
863 updateArtifactFacets( artifact, properties );
867 writeProperties( properties, directory, PROJECT_VERSION_METADATA_KEY );
869 catch ( IOException e )
872 log.error( e.getMessage(), e );
876 private Properties readOrCreateProperties( File directory, String propertiesKey )
880 return readProperties( directory, propertiesKey );
882 catch ( FileNotFoundException | NoSuchFileException e )
884 // ignore and return new properties
886 catch ( IOException e )
889 log.error( e.getMessage(), e );
891 return new Properties();
894 private Properties readProperties( File directory, String propertiesKey )
897 Properties properties = new Properties();
898 try (InputStream in = Files.newInputStream( new File( directory, propertiesKey + ".properties" ).toPath() ))
901 properties.load( in );
907 public ProjectMetadata getProject( String repoId, String namespace, String projectId )
909 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId );
911 Properties properties = readOrCreateProperties( directory, PROJECT_METADATA_KEY );
913 ProjectMetadata project = null;
915 String id = properties.getProperty( "id" );
918 project = new ProjectMetadata();
919 project.setNamespace( properties.getProperty( "namespace" ) );
927 public ProjectVersionMetadata getProjectVersion( String repoId, String namespace, String projectId,
928 String projectVersion )
930 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
932 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
933 String id = properties.getProperty( "id" );
934 ProjectVersionMetadata versionMetadata = null;
937 versionMetadata = new ProjectVersionMetadata();
938 versionMetadata.setId( id );
939 versionMetadata.setName( properties.getProperty( "name" ) );
940 versionMetadata.setDescription( properties.getProperty( "description" ) );
941 versionMetadata.setUrl( properties.getProperty( "url" ) );
942 versionMetadata.setIncomplete( Boolean.valueOf( properties.getProperty( "incomplete", "false" ) ) );
944 String scmConnection = properties.getProperty( "scm.connection" );
945 String scmDeveloperConnection = properties.getProperty( "scm.developerConnection" );
946 String scmUrl = properties.getProperty( "scm.url" );
947 if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
950 scm.setConnection( scmConnection );
951 scm.setDeveloperConnection( scmDeveloperConnection );
952 scm.setUrl( scmUrl );
953 versionMetadata.setScm( scm );
956 String ciSystem = properties.getProperty( "ci.system" );
957 String ciUrl = properties.getProperty( "ci.url" );
958 if ( ciSystem != null || ciUrl != null )
960 CiManagement ci = new CiManagement();
961 ci.setSystem( ciSystem );
963 versionMetadata.setCiManagement( ci );
966 String issueSystem = properties.getProperty( "issue.system" );
967 String issueUrl = properties.getProperty( "issue.url" );
968 if ( issueSystem != null || issueUrl != null )
970 IssueManagement issueManagement = new IssueManagement();
971 issueManagement.setSystem( issueSystem );
972 issueManagement.setUrl( issueUrl );
973 versionMetadata.setIssueManagement( issueManagement );
976 String orgName = properties.getProperty( "org.name" );
977 String orgUrl = properties.getProperty( "org.url" );
978 if ( orgName != null || orgUrl != null )
980 Organization org = new Organization();
981 org.setName( orgName );
982 org.setUrl( orgUrl );
983 versionMetadata.setOrganization( org );
986 boolean done = false;
990 String licenseName = properties.getProperty( "license." + i + ".name" );
991 String licenseUrl = properties.getProperty( "license." + i + ".url" );
992 if ( licenseName != null || licenseUrl != null )
994 License license = new License();
995 license.setName( licenseName );
996 license.setUrl( licenseUrl );
997 versionMetadata.addLicense( license );
1010 String mailingListName = properties.getProperty( "mailingList." + i + ".name" );
1011 if ( mailingListName != null )
1013 MailingList mailingList = new MailingList();
1014 mailingList.setName( mailingListName );
1015 mailingList.setMainArchiveUrl( properties.getProperty( "mailingList." + i + ".archive" ) );
1016 String p = properties.getProperty( "mailingList." + i + ".otherArchives" );
1017 if ( p != null && p.length() > 0 )
1019 mailingList.setOtherArchives( Arrays.asList( p.split( "," ) ) );
1023 mailingList.setOtherArchives( Collections.<String>emptyList() );
1025 mailingList.setPostAddress( properties.getProperty( "mailingList." + i + ".post" ) );
1026 mailingList.setSubscribeAddress( properties.getProperty( "mailingList." + i + ".subscribe" ) );
1027 mailingList.setUnsubscribeAddress( properties.getProperty( "mailingList." + i + ".unsubscribe" ) );
1028 versionMetadata.addMailingList( mailingList );
1041 String dependencyArtifactId = properties.getProperty( "dependency." + i + ".artifactId" );
1042 if ( dependencyArtifactId != null )
1044 Dependency dependency = new Dependency();
1045 dependency.setArtifactId( dependencyArtifactId );
1046 dependency.setGroupId( properties.getProperty( "dependency." + i + ".groupId" ) );
1047 dependency.setClassifier( properties.getProperty( "dependency." + i + ".classifier" ) );
1048 dependency.setOptional(
1049 Boolean.valueOf( properties.getProperty( "dependency." + i + ".optional" ) ) );
1050 dependency.setScope( properties.getProperty( "dependency." + i + ".scope" ) );
1051 dependency.setSystemPath( properties.getProperty( "dependency." + i + ".systemPath" ) );
1052 dependency.setType( properties.getProperty( "dependency." + i + ".type" ) );
1053 dependency.setVersion( properties.getProperty( "dependency." + i + ".version" ) );
1054 dependency.setOptional(
1055 Boolean.valueOf( properties.getProperty( "dependency." + i + ".optional" ) ) );
1056 versionMetadata.addDependency( dependency );
1065 String facetIds = properties.getProperty( "facetIds", "" );
1066 if ( facetIds.length() > 0 )
1068 for ( String facetId : facetIds.split( "," ) )
1070 MetadataFacetFactory factory = metadataFacetFactories.get( facetId );
1071 if ( factory == null )
1073 log.error( "Attempted to load unknown project version metadata facet: {}", facetId );
1077 MetadataFacet facet = factory.createMetadataFacet();
1078 Map<String, String> map = new HashMap<>();
1079 for ( Object key : new ArrayList( properties.keySet() ) )
1081 String property = (String) key;
1082 if ( property.startsWith( facet.getFacetId() ) )
1084 map.put( property.substring( facet.getFacetId().length() + 1 ),
1085 properties.getProperty( property ) );
1088 facet.fromProperties( map );
1089 versionMetadata.addFacet( facet );
1094 updateProjectVersionFacets( versionMetadata, properties );
1096 return versionMetadata;
1100 public Collection<String> getArtifactVersions( String repoId, String namespace, String projectId,
1101 String projectVersion )
1103 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
1105 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
1107 Set<String> versions = new HashSet<String>();
1108 for ( Map.Entry entry : properties.entrySet() )
1110 String name = (String) entry.getKey();
1111 if ( name.startsWith( "artifact:version:" ) )
1113 versions.add( (String) entry.getValue() );
1120 public Collection<ProjectVersionReference> getProjectReferences( String repoId, String namespace, String projectId,
1121 String projectVersion )
1123 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
1125 Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
1126 int numberOfRefs = Integer.parseInt( properties.getProperty( "ref:lastReferenceNum", "-1" ) ) + 1;
1128 List<ProjectVersionReference> references = new ArrayList<>();
1129 for ( int i = 0; i < numberOfRefs; i++ )
1131 ProjectVersionReference reference = new ProjectVersionReference();
1132 reference.setProjectId( properties.getProperty( "ref:reference." + i + ".projectId" ) );
1133 reference.setNamespace( properties.getProperty( "ref:reference." + i + ".namespace" ) );
1134 reference.setProjectVersion( properties.getProperty( "ref:reference." + i + ".projectVersion" ) );
1135 reference.setReferenceType( ProjectVersionReference.ReferenceType.valueOf(
1136 properties.getProperty( "ref:reference." + i + ".referenceType" ) ) );
1137 references.add( reference );
1143 public Collection<String> getRootNamespaces( String repoId )
1145 return getNamespaces( repoId, null );
1149 public Collection<String> getNamespaces( String repoId, String baseNamespace )
1151 List<String> allNamespaces = new ArrayList<>();
1152 File directory = getDirectory( repoId );
1153 File[] files = directory.listFiles();
1154 if ( files != null )
1156 for ( File namespace : files )
1158 if ( new File( namespace, NAMESPACE_METADATA_KEY + ".properties" ).exists() )
1160 allNamespaces.add( namespace.getName() );
1165 Set<String> namespaces = new LinkedHashSet<>();
1166 int fromIndex = baseNamespace != null ? baseNamespace.length() + 1 : 0;
1167 for ( String namespace : allNamespaces )
1169 if ( baseNamespace == null || namespace.startsWith( baseNamespace + "." ) )
1171 int i = namespace.indexOf( '.', fromIndex );
1174 namespaces.add( namespace.substring( fromIndex, i ) );
1178 namespaces.add( namespace.substring( fromIndex ) );
1182 return new ArrayList<>( namespaces );
1186 public Collection<String> getProjects( String repoId, String namespace )
1188 List<String> projects = new ArrayList<>();
1189 File directory = new File( getDirectory( repoId ), namespace );
1190 File[] files = directory.listFiles();
1191 if ( files != null )
1193 for ( File project : files )
1195 if ( new File( project, PROJECT_METADATA_KEY + ".properties" ).exists() )
1197 projects.add( project.getName() );
1205 public Collection<String> getProjectVersions( String repoId, String namespace, String projectId )
1207 List<String> projectVersions = new ArrayList<>();
1208 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId );
1209 File[] files = directory.listFiles();
1210 if ( files != null )
1212 for ( File projectVersion : files )
1214 if ( new File( projectVersion, PROJECT_VERSION_METADATA_KEY + ".properties" ).exists() )
1216 projectVersions.add( projectVersion.getName() );
1220 return projectVersions;
1224 public void removeProject( String repositoryId, String namespace, String projectId )
1225 throws MetadataRepositoryException
1227 File directory = new File( getDirectory( repositoryId ), namespace + "/" + projectId );
1230 if ( directory.exists() )
1232 FileUtils.deleteDirectory( directory );
1235 catch ( IOException e )
1237 throw new MetadataRepositoryException( e.getMessage(), e );
1242 public void removeProjectVersion( String repoId, String namespace, String projectId, String projectVersion )
1243 throws MetadataRepositoryException
1245 File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
1246 if ( directory.exists() )
1250 FileUtils.deleteDirectory( directory );
1252 catch ( IOException e )
1254 throw new MetadataRepositoryException( e.getMessage(), e );
1259 private void writeProperties( Properties properties, File directory, String propertiesKey )
1263 try (OutputStream os = Files.newOutputStream( new File( directory, propertiesKey + ".properties" ).toPath() ))
1265 properties.store( os, null );
1269 private static class ArtifactComparator
1270 implements Comparator<ArtifactMetadata>
1273 public int compare( ArtifactMetadata artifact1, ArtifactMetadata artifact2 )
1275 if ( artifact1.getWhenGathered() == artifact2.getWhenGathered() )
1279 if ( artifact1.getWhenGathered() == null )
1283 if ( artifact2.getWhenGathered() == null )
1287 return artifact1.getWhenGathered().compareTo( artifact2.getWhenGathered() );
1292 public List<ArtifactMetadata> getArtifacts( String repoId )
1294 List<ArtifactMetadata> artifacts = new ArrayList<>();
1295 for ( String ns : getRootNamespaces( repoId ) )
1297 getArtifacts( artifacts, repoId, ns );
1302 private void getArtifacts( List<ArtifactMetadata> artifacts, String repoId, String ns )
1304 for ( String namespace : getNamespaces( repoId, ns ) )
1306 getArtifacts( artifacts, repoId, ns + "." + namespace );
1309 for ( String project : getProjects( repoId, ns ) )
1311 for ( String version : getProjectVersions( repoId, ns, project ) )
1313 for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) )
1315 artifacts.add( artifact );
1322 public List<ArtifactMetadata> searchArtifacts( String text, String repositoryId, boolean exact )
1323 throws MetadataRepositoryException
1325 throw new UnsupportedOperationException( "searchArtifacts not yet implemented in File backend" );
1329 public List<ArtifactMetadata> searchArtifacts( String key, String text, String repositoryId, boolean exact )
1330 throws MetadataRepositoryException
1332 throw new UnsupportedOperationException( "searchArtifacts not yet implemented in File backend" );