You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@archiva.apache.org by br...@apache.org on 2010/12/17 07:26:10 UTC

svn commit: r1050283 [1/2] - in /archiva/trunk: ./ archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/ archiva-modules/plugins/ archiva-modules/plugins/metadata-repository-file/src/main/java/org/apache...

Author: brett
Date: Fri Dec 17 06:26:09 2010
New Revision: 1050283

URL: http://svn.apache.org/viewvc?rev=1050283&view=rev
Log:
[MRM-1327] very rough prototype of a JCR based storage mechanism. Passes tests, but needs to be cleaned up and properly integrated, then tested for performance

Added:
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/   (with props)
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/pom.xml
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/main/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/main/java/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepository.java
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/test/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/test/java/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/jcr/
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepositoryTest.java
    archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/test/repository.xml
Modified:
    archiva/trunk/archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/AbstractMetadataRepositoryTest.java
    archiva/trunk/archiva-modules/plugins/metadata-repository-file/src/main/java/org/apache/archiva/metadata/repository/file/FileMetadataRepository.java
    archiva/trunk/archiva-modules/plugins/pom.xml
    archiva/trunk/pom.xml

Modified: archiva/trunk/archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/AbstractMetadataRepositoryTest.java
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/AbstractMetadataRepositoryTest.java?rev=1050283&r1=1050282&r2=1050283&view=diff
==============================================================================
--- archiva/trunk/archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/AbstractMetadataRepositoryTest.java (original)
+++ archiva/trunk/archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/AbstractMetadataRepositoryTest.java Fri Dec 17 06:26:09 2010
@@ -20,11 +20,18 @@ package org.apache.archiva.metadata.repo
  */
 
 import org.apache.archiva.metadata.model.ArtifactMetadata;
+import org.apache.archiva.metadata.model.CiManagement;
+import org.apache.archiva.metadata.model.Dependency;
+import org.apache.archiva.metadata.model.IssueManagement;
+import org.apache.archiva.metadata.model.License;
 import org.apache.archiva.metadata.model.MailingList;
 import org.apache.archiva.metadata.model.MetadataFacet;
 import org.apache.archiva.metadata.model.MetadataFacetFactory;
+import org.apache.archiva.metadata.model.Organization;
 import org.apache.archiva.metadata.model.ProjectMetadata;
 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
+import org.apache.archiva.metadata.model.ProjectVersionReference;
+import org.apache.archiva.metadata.model.Scm;
 import org.codehaus.plexus.spring.PlexusInSpringTestCase;
 
 import java.util.ArrayList;
@@ -102,7 +109,87 @@ public abstract class AbstractMetadataRe
         assertEquals( Collections.<String>emptyList(), namespaces );
     }
 
+    public void testGetNamespaceOnly()
+    {
+        assertEquals( Collections.emptyList(), repository.getRootNamespaces( TEST_REPO_ID ) );
+
+        repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE );
+
+        assertEquals( Collections.singletonList( TEST_NAMESPACE ), repository.getRootNamespaces( TEST_REPO_ID ) );
+    }
+
+    public void testGetProjectOnly()
+    {
+        assertNull( repository.getProject( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT ) );
+        assertEquals( Collections.emptyList(), repository.getRootNamespaces( TEST_REPO_ID ) );
+
+        ProjectMetadata project = new ProjectMetadata();
+        project.setId( TEST_PROJECT );
+        project.setNamespace( TEST_NAMESPACE );
+
+        repository.updateProject( TEST_REPO_ID, project );
+
+        project = repository.getProject( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT );
+        assertEquals( TEST_PROJECT, project.getId() );
+        assertEquals( TEST_NAMESPACE, project.getNamespace() );
+
+        // test that namespace is also constructed
+        assertEquals( Collections.singletonList( TEST_NAMESPACE ), repository.getRootNamespaces( TEST_REPO_ID ) );
+    }
+
+    public void testGetProjectVersionOnly()
+        throws MetadataResolutionException
+    {
+        assertNull( repository.getProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION ) );
+        assertNull( repository.getProject( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT ) );
+        assertEquals( Collections.<String>emptyList(), repository.getRootNamespaces( TEST_REPO_ID ) );
+
+        ProjectVersionMetadata metadata = new ProjectVersionMetadata();
+        metadata.setId( TEST_PROJECT_VERSION );
+
+        repository.updateProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, metadata );
+
+        metadata = repository.getProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION );
+        assertEquals( TEST_PROJECT_VERSION, metadata.getId() );
+
+        // test that namespace and project is also constructed
+        assertEquals( Collections.singletonList( TEST_NAMESPACE ), repository.getRootNamespaces( TEST_REPO_ID ) );
+        ProjectMetadata projectMetadata = repository.getProject( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT );
+        assertEquals( TEST_PROJECT, projectMetadata.getId() );
+        assertEquals( TEST_NAMESPACE, projectMetadata.getNamespace() );
+    }
+
+    public void testGetArtifactOnly()
+        throws MetadataResolutionException
+    {
+        assertEquals( Collections.<ArtifactMetadata>emptyList(),
+                      new ArrayList<ArtifactMetadata>(
+                          repository.getArtifacts( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION ) ) );
+        assertNull( repository.getProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION ) );
+        assertNull( repository.getProject( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT ) );
+        assertEquals( Collections.<String>emptyList(), repository.getRootNamespaces( TEST_REPO_ID ) );
+
+        ArtifactMetadata metadata = createArtifact();
+
+        repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, metadata );
+
+        Collection<ArtifactMetadata> artifacts = repository.getArtifacts( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT,
+                                                                          TEST_PROJECT_VERSION );
+        assertEquals( Collections.singletonList( metadata ), new ArrayList<ArtifactMetadata>( artifacts ) );
+
+        // test that namespace, project and project version is also constructed
+        assertEquals( Collections.singletonList( TEST_NAMESPACE ), repository.getRootNamespaces( TEST_REPO_ID ) );
+
+        ProjectMetadata projectMetadata = repository.getProject( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT );
+        assertEquals( TEST_PROJECT, projectMetadata.getId() );
+        assertEquals( TEST_NAMESPACE, projectMetadata.getNamespace() );
+
+        ProjectVersionMetadata projectVersionMetadata = repository.getProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION );
+        assertEquals( TEST_PROJECT_VERSION, projectVersionMetadata.getId() );
+    }
+
     public void testUpdateProjectVersionMetadataWithNoOtherArchives()
+        throws MetadataResolutionException
     {
         ProjectVersionMetadata metadata = new ProjectVersionMetadata();
         metadata.setId( TEST_PROJECT_VERSION );
@@ -111,6 +198,134 @@ public abstract class AbstractMetadataRe
         mailingList.setOtherArchives( Collections.<String>emptyList() );
         metadata.setMailingLists( Collections.singletonList( mailingList ) );
         repository.updateProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, metadata );
+
+        metadata = repository.getProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION );
+        assertEquals( TEST_PROJECT_VERSION, metadata.getId() );
+        assertEquals( 1, metadata.getMailingLists().size() );
+        mailingList = metadata.getMailingLists().get( 0 );
+        assertEquals( "Foo List", mailingList.getName() );
+        assertEquals( Collections.<String>emptyList(), mailingList.getOtherArchives() );
+    }
+
+    public void testUpdateProjectVersionMetadataWithAllElements()
+        throws MetadataResolutionException
+    {
+        ProjectVersionMetadata metadata = new ProjectVersionMetadata();
+        metadata.setId( TEST_PROJECT_VERSION );
+
+        metadata.setName( "project name" );
+        metadata.setDescription( "project description" );
+
+        MailingList mailingList = new MailingList();
+        mailingList.setName( "Foo List" );
+        mailingList.setOtherArchives( Collections.singletonList( "other archive" ) );
+        metadata.setMailingLists( Collections.singletonList( mailingList ) );
+
+        Scm scm = new Scm();
+        scm.setConnection( "connection" );
+        scm.setDeveloperConnection( "dev conn" );
+        scm.setUrl( "url" );
+        metadata.setScm( scm );
+
+        CiManagement ci = new CiManagement();
+        ci.setSystem( "system" );
+        ci.setUrl( "ci url" );
+        metadata.setCiManagement( ci );
+
+        IssueManagement tracker = new IssueManagement();
+        tracker.setSystem( "system" );
+        tracker.setUrl( "issue tracker url" );
+        metadata.setIssueManagement( tracker );
+
+        Organization org = new Organization();
+        org.setName( "org name" );
+        org.setUrl( "url" );
+        metadata.setOrganization( org );
+
+        License l = new License();
+        l.setName( "license name" );
+        l.setUrl( "license url" );
+        metadata.addLicense( l );
+
+        Dependency d = new Dependency();
+        d.setArtifactId( "artifactId" );
+        d.setClassifier( "classifier" );
+        d.setGroupId( "groupId" );
+        d.setScope( "scope" );
+        d.setSystemPath( "system path" );
+        d.setType( "type" );
+        d.setVersion( "version" );
+        metadata.addDependency( d );
+
+        repository.updateProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, metadata );
+
+        metadata = repository.getProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION );
+        assertEquals( TEST_PROJECT_VERSION, metadata.getId() );
+        assertEquals( TEST_PROJECT_VERSION, metadata.getVersion() );
+        assertEquals( "project name", metadata.getName() );
+        assertEquals( "project description", metadata.getDescription() );
+
+        assertEquals( 1, metadata.getMailingLists().size() );
+        mailingList = metadata.getMailingLists().get( 0 );
+        assertEquals( "Foo List", mailingList.getName() );
+        assertEquals( Collections.singletonList( "other archive" ), mailingList.getOtherArchives() );
+
+        assertEquals( "connection", metadata.getScm().getConnection() );
+        assertEquals( "dev conn", metadata.getScm().getDeveloperConnection() );
+        assertEquals( "url", metadata.getScm().getUrl() );
+
+        assertEquals( "system", metadata.getCiManagement().getSystem() );
+        assertEquals( "ci url", metadata.getCiManagement().getUrl() );
+
+        assertEquals( "system", metadata.getIssueManagement().getSystem() );
+        assertEquals( "issue tracker url", metadata.getIssueManagement().getUrl() );
+
+        assertEquals( "org name", metadata.getOrganization().getName() );
+        assertEquals( "url", metadata.getOrganization().getUrl() );
+
+        assertEquals( 1, metadata.getLicenses().size() );
+        l = metadata.getLicenses().get( 0 );
+        assertEquals( "license name", l.getName() );
+        assertEquals( "license url", l.getUrl() );
+
+        assertEquals( 1, metadata.getDependencies().size() );
+        d = metadata.getDependencies().get( 0 );
+        assertEquals( "artifactId", d.getArtifactId() );
+        assertEquals( "classifier", d.getClassifier() );
+        assertEquals( "groupId", d.getGroupId() );
+        assertEquals( "scope", d.getScope() );
+        assertEquals( "system path", d.getSystemPath() );
+        assertEquals( "type", d.getType() );
+        assertEquals( "version", d.getVersion() );
+    }
+
+    public void testUpdateProjectReference()
+    {
+        ProjectVersionReference reference = new ProjectVersionReference();
+        reference.setNamespace( "another.namespace" );
+        reference.setProjectId( "another-project-id" );
+        reference.setProjectVersion( "1.1" );
+        reference.setReferenceType( ProjectVersionReference.ReferenceType.DEPENDENCY );
+
+        repository.updateProjectReference( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, reference );
+
+        Collection<ProjectVersionReference> references = repository.getProjectReferences( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION );
+        assertEquals( 1, references.size() );
+        reference = references.iterator().next();
+        assertEquals( "another.namespace", reference.getNamespace() );
+        assertEquals( "another-project-id", reference.getProjectId() );
+        assertEquals( "1.1", reference.getProjectVersion() );
+        assertEquals( ProjectVersionReference.ReferenceType.DEPENDENCY, reference.getReferenceType() );
+    }
+
+    public void testGetRepositories()
+    {
+        // currently set up this way so the behaviour of both the test and the mock config return the same repository
+        // set as the File implementation just uses the config rather than the content
+        repository.updateNamespace( TEST_REPO_ID, "namespace" );
+        repository.updateNamespace( "other-repo", "namespace" );
+
+        assertEquals( Arrays.asList( TEST_REPO_ID, "other-repo" ), new ArrayList<String>( repository.getRepositories() ) );
     }
 
     public void testUpdateProjectVersionMetadataIncomplete()
@@ -123,6 +338,17 @@ public abstract class AbstractMetadataRe
 
         metadata = repository.getProjectVersion( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION );
         assertEquals( true, metadata.isIncomplete() );
+        assertNull( metadata.getCiManagement() );
+        assertNull( metadata.getScm() );
+        assertNull( metadata.getIssueManagement() );
+        assertNull( metadata.getOrganization() );
+        assertNull( metadata.getDescription() );
+        assertNull( metadata.getName() );
+        assertEquals( TEST_PROJECT_VERSION, metadata.getId() );
+        assertEquals( TEST_PROJECT_VERSION, metadata.getVersion() );
+        assertTrue( metadata.getMailingLists().isEmpty() );
+        assertTrue( metadata.getLicenses().isEmpty() );
+        assertTrue( metadata.getDependencies().isEmpty() );
     }
 
     public void testUpdateProjectVersionMetadataWithExistingFacets()
@@ -311,9 +537,16 @@ public abstract class AbstractMetadataRe
 
     public void testRemoveFacetsWhenUnknown()
     {
+        // testing no exception
         repository.removeMetadataFacets( TEST_REPO_ID, UNKNOWN );
     }
 
+    public void testRemoveFacetWhenUnknown()
+    {
+        // testing no exception
+        repository.removeMetadataFacet( TEST_REPO_ID, UNKNOWN, TEST_NAME );
+    }
+
     public void testRemoveFacet()
     {
         TestMetadataFacet metadataFacet = new TestMetadataFacet( TEST_VALUE );
@@ -343,11 +576,6 @@ public abstract class AbstractMetadataRe
         assertNull( repository.getMetadataFacet( TEST_REPO_ID, TEST_FACET_ID, TEST_NAME ) );
     }
 
-    public void testRemoveFacetWhenUnknown()
-    {
-        repository.removeMetadataFacet( TEST_REPO_ID, UNKNOWN, TEST_NAME );
-    }
-        
     public void testGetArtifacts()
     {
         ArtifactMetadata artifact1 = createArtifact();
@@ -405,8 +633,6 @@ public abstract class AbstractMetadataRe
 
     public void testGetArtifactsByDateRangeOpen()
     {
-        repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE );
-        repository.updateProject( TEST_REPO_ID, createProject() );
         ArtifactMetadata artifact = createArtifact();
         repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
 
@@ -417,8 +643,6 @@ public abstract class AbstractMetadataRe
     public void testGetArtifactsByDateRangeSparseNamespace()
     {
         String namespace = "org.apache.archiva";
-        repository.updateNamespace( TEST_REPO_ID, namespace );
-        repository.updateProject( TEST_REPO_ID, createProject( namespace ) );
         ArtifactMetadata artifact = createArtifact();
         artifact.setNamespace( namespace );
         repository.updateArtifact( TEST_REPO_ID, namespace, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
@@ -429,8 +653,6 @@ public abstract class AbstractMetadataRe
 
     public void testGetArtifactsByDateRangeLowerBound()
     {
-        repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE );
-        repository.updateProject( TEST_REPO_ID, createProject() );
         ArtifactMetadata artifact = createArtifact();
         repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
 
@@ -441,8 +663,6 @@ public abstract class AbstractMetadataRe
 
     public void testGetArtifactsByDateRangeLowerBoundOutOfRange()
     {
-        repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE );
-        repository.updateProject( TEST_REPO_ID, createProject() );
         ArtifactMetadata artifact = createArtifact();
         repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
 
@@ -452,8 +672,6 @@ public abstract class AbstractMetadataRe
 
     public void testGetArtifactsByDateRangeLowerAndUpperBound()
     {
-        repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE );
-        repository.updateProject( TEST_REPO_ID, createProject() );
         ArtifactMetadata artifact = createArtifact();
         repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
 
@@ -465,8 +683,6 @@ public abstract class AbstractMetadataRe
 
     public void testGetArtifactsByDateRangeUpperBound()
     {
-        repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE );
-        repository.updateProject( TEST_REPO_ID, createProject() );
         ArtifactMetadata artifact = createArtifact();
         repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
 
@@ -477,8 +693,6 @@ public abstract class AbstractMetadataRe
 
     public void testGetArtifactsByDateRangeUpperBoundOutOfRange()
     {
-        repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE );
-        repository.updateProject( TEST_REPO_ID, createProject() );
         ArtifactMetadata artifact = createArtifact();
         repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
 
@@ -488,13 +702,9 @@ public abstract class AbstractMetadataRe
 
      public void testGetArtifactsByRepoId()
     {
-        repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE );
-        repository.updateProject( TEST_REPO_ID, createProject() );
         ArtifactMetadata artifact = createArtifact();
         repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
-        assertFalse( repository.getArtifacts(TEST_REPO_ID).isEmpty());
-
-
+        assertEquals( Collections.singletonList( artifact ), repository.getArtifacts( TEST_REPO_ID ) );
     }
 
     public void testGetNamespacesWithSparseDepth()
@@ -509,8 +719,6 @@ public abstract class AbstractMetadataRe
 
     public void testGetArtifactsByChecksumSingleResultMd5()
     {
-        repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE );
-        repository.updateProject( TEST_REPO_ID, createProject() );
         ArtifactMetadata artifact = createArtifact();
         repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
 
@@ -520,8 +728,6 @@ public abstract class AbstractMetadataRe
 
     public void testGetArtifactsByChecksumSingleResultSha1()
     {
-        repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE );
-        repository.updateProject( TEST_REPO_ID, createProject() );
         ArtifactMetadata artifact = createArtifact();
         repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
 
@@ -529,19 +735,23 @@ public abstract class AbstractMetadataRe
                                                                                                 TEST_SHA1 ) );
     }
 
-    public void testGetArtifactsByChecksumMultipleResult()
+    public void testGetArtifactsByChecksumDeepNamespace()
     {
-        repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE );
+        ArtifactMetadata artifact = createArtifact();
+        String namespace = "multi.level.ns";
+        artifact.setNamespace( namespace );
+        repository.updateArtifact( TEST_REPO_ID, namespace, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
 
-        ProjectMetadata projectMetadata = createProject();
-        repository.updateProject( TEST_REPO_ID, projectMetadata );
+        assertEquals( Collections.singletonList( artifact ), repository.getArtifactsByChecksum( TEST_REPO_ID,
+                                                                                                TEST_SHA1 ) );
+    }
+
+    public void testGetArtifactsByChecksumMultipleResult()
+    {
         ArtifactMetadata artifact1 = createArtifact();
         repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact1 );
 
-        projectMetadata = createProject();
         String newProjectId = "another-project";
-        projectMetadata.setId( newProjectId );
-        repository.updateProject( TEST_REPO_ID, projectMetadata );
         ArtifactMetadata artifact2 = createArtifact();
         artifact2.setProject( newProjectId );
         repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, newProjectId, TEST_PROJECT_VERSION, artifact2 );
@@ -554,18 +764,17 @@ public abstract class AbstractMetadataRe
 
     public void testGetArtifactsByChecksumNoResult()
     {
-        repository.updateNamespace( TEST_REPO_ID, TEST_NAMESPACE );
-        repository.updateProject( TEST_REPO_ID, createProject() );
         ArtifactMetadata artifact = createArtifact();
         repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
 
-        assertEquals( Collections.<ArtifactMetadata>emptyList(), repository.getArtifactsByChecksum( TEST_REPO_ID,
-                                                                                                    "not a checksum" ) );
+        assertEquals( Collections.<ArtifactMetadata>emptyList(), repository.getArtifactsByChecksum( TEST_REPO_ID, "not a checksum" ) );
     }
 
     public void testDeleteArtifact()
     {
         ArtifactMetadata artifact = createArtifact();
+        artifact.addFacet( new TestMetadataFacet( "value" ) );
+
         repository.updateArtifact( TEST_REPO_ID, TEST_NAMESPACE, TEST_PROJECT, TEST_PROJECT_VERSION, artifact );
 
         assertEquals( Collections.singletonList( artifact ), new ArrayList<ArtifactMetadata>( repository.getArtifacts(
@@ -608,15 +817,16 @@ public abstract class AbstractMetadataRe
 
         repository.deleteRepository( TEST_REPO_ID );
 
-        assertTrue( repository.getArtifactsByDateRange( TEST_REPO_ID, null, null ).isEmpty() );
+        assertTrue( repository.getArtifacts( TEST_REPO_ID ).isEmpty() );
+        assertTrue( repository.getRootNamespaces( TEST_REPO_ID ).isEmpty() );
     }
 
-    private ProjectMetadata createProject()
+    private static ProjectMetadata createProject()
     {
         return createProject( TEST_NAMESPACE );
     }
 
-    private ProjectMetadata createProject( String ns )
+    private static ProjectMetadata createProject( String ns )
     {
         ProjectMetadata project = new ProjectMetadata();
         project.setId( TEST_PROJECT );
@@ -624,12 +834,12 @@ public abstract class AbstractMetadataRe
         return project;
     }
 
-    private ArtifactMetadata createArtifact()
+    private static ArtifactMetadata createArtifact()
     {
         return createArtifact( "jar" );
     }
 
-    private ArtifactMetadata createArtifact( String type )
+    private static ArtifactMetadata createArtifact( String type )
     {
         ArtifactMetadata artifact = new ArtifactMetadata();
         artifact.setId( TEST_PROJECT + "-" + TEST_PROJECT_VERSION + "." + type );
@@ -645,7 +855,7 @@ public abstract class AbstractMetadataRe
         return artifact;
     }
 
-    private class ArtifactMetadataComparator
+    private static class ArtifactMetadataComparator
         implements Comparator<ArtifactMetadata>
     {
         public final int compare( ArtifactMetadata a, ArtifactMetadata b )

Modified: archiva/trunk/archiva-modules/plugins/metadata-repository-file/src/main/java/org/apache/archiva/metadata/repository/file/FileMetadataRepository.java
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/plugins/metadata-repository-file/src/main/java/org/apache/archiva/metadata/repository/file/FileMetadataRepository.java?rev=1050283&r1=1050282&r2=1050283&view=diff
==============================================================================
--- archiva/trunk/archiva-modules/plugins/metadata-repository-file/src/main/java/org/apache/archiva/metadata/repository/file/FileMetadataRepository.java (original)
+++ archiva/trunk/archiva-modules/plugins/metadata-repository-file/src/main/java/org/apache/archiva/metadata/repository/file/FileMetadataRepository.java Fri Dec 17 06:26:09 2010
@@ -133,7 +133,7 @@ public class FileMetadataRepository
 
         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
         // remove properties that are not references or artifacts
-        for ( Object key : new ArrayList( properties.keySet() ) )
+        for ( Object key : new ArrayList<Object>( properties.keySet() ) )
         {
             String name = (String) key;
             if ( !name.contains( ":" ) && !name.equals( "facetIds" ) )
@@ -579,7 +579,7 @@ public class FileMetadataRepository
         properties.remove( "artifact:facetIds:" + id );
 
         String prefix = "artifact:facet:" + id + ":";
-        for ( Object key : new ArrayList( properties.keySet() ) )
+        for ( Object key : new ArrayList<Object>( properties.keySet() ) )
         {
             String property = (String) key;
             if ( property.startsWith( prefix ) )
@@ -666,6 +666,10 @@ public class FileMetadataRepository
     public void updateArtifact( String repoId, String namespace, String projectId, String projectVersion,
                                 ArtifactMetadata artifact )
     {
+        ProjectVersionMetadata metadata = new ProjectVersionMetadata();
+        metadata.setId( projectVersion );
+        updateProjectVersion( repoId, namespace, projectId, metadata );
+
         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId + "/" + projectVersion );
 
         Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
@@ -741,11 +745,18 @@ public class FileMetadataRepository
     {
         File directory = new File( getDirectory( repoId ), namespace + "/" + projectId );
 
-        Properties properties = readOrCreateProperties( directory, PROJECT_VERSION_METADATA_KEY );
+        Properties properties = readOrCreateProperties( directory, PROJECT_METADATA_KEY );
+
+        ProjectMetadata project = null;
+
+        String id = properties.getProperty( "id" );
+        if ( id != null )
+        {
+            project = new ProjectMetadata();
+            project.setNamespace( properties.getProperty( "namespace" ) );
+            project.setId( id );
+        }
 
-        ProjectMetadata project = new ProjectMetadata();
-        project.setNamespace( properties.getProperty( "namespace" ) );
-        project.setId( properties.getProperty( "id" ) );
         return project;
     }
 
@@ -838,8 +849,15 @@ public class FileMetadataRepository
                     MailingList mailingList = new MailingList();
                     mailingList.setName( mailingListName );
                     mailingList.setMainArchiveUrl( properties.getProperty( "mailingList." + i + ".archive" ) );
-                    mailingList.setOtherArchives( Arrays.asList( properties.getProperty(
-                        "mailingList." + i + ".otherArchives" ).split( "," ) ) );
+                    String p = properties.getProperty( "mailingList." + i + ".otherArchives" );
+                    if ( p != null && p.length() > 0 )
+                    {
+                        mailingList.setOtherArchives( Arrays.asList( p.split( "," ) ) );
+                    }
+                    else
+                    {
+                        mailingList.setOtherArchives( Collections.<String>emptyList() );
+                    }
                     mailingList.setPostAddress( properties.getProperty( "mailingList." + i + ".post" ) );
                     mailingList.setSubscribeAddress( properties.getProperty( "mailingList." + i + ".subscribe" ) );
                     mailingList.setUnsubscribeAddress( properties.getProperty( "mailingList." + i + ".unsubscribe" ) );
@@ -1078,7 +1096,6 @@ public class FileMetadataRepository
 
     public List<ArtifactMetadata> getArtifacts( String repoId )
     {
-
         List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
         for ( String ns : getRootNamespaces( repoId ) )
         {
@@ -1100,9 +1117,7 @@ public class FileMetadataRepository
             {
                 for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) )
                 {
-
                     artifacts.add( artifact );
-
                 }
             }
         }

Propchange: archiva/trunk/archiva-modules/plugins/metadata-store-jcr/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Fri Dec 17 06:26:09 2010
@@ -0,0 +1 @@
+*.iml

Added: archiva/trunk/archiva-modules/plugins/metadata-store-jcr/pom.xml
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/plugins/metadata-store-jcr/pom.xml?rev=1050283&view=auto
==============================================================================
--- archiva/trunk/archiva-modules/plugins/metadata-store-jcr/pom.xml (added)
+++ archiva/trunk/archiva-modules/plugins/metadata-store-jcr/pom.xml Fri Dec 17 06:26:09 2010
@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one
+  ~ or more contributor license agreements.  See the NOTICE file
+  ~ distributed with this work for additional information
+  ~ regarding copyright ownership.  The ASF licenses this file
+  ~ to you under the Apache License, Version 2.0 (the
+  ~ "License"); you may not use this file except in compliance
+  ~ with the License.  You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing,
+  ~ software distributed under the License is distributed on an
+  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+  ~ KIND, either express or implied.  See the License for the
+  ~ specific language governing permissions and limitations
+  ~ under the License.
+  -->
+
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
+         xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+  <modelVersion>4.0.0</modelVersion>
+  <parent>
+    <artifactId>plugins</artifactId>
+    <groupId>org.apache.archiva</groupId>
+    <version>1.4-SNAPSHOT</version>
+  </parent>
+  <artifactId>metadata-store-jcr</artifactId>
+  <name>JCR Storage for Metadata</name>
+  <dependencies>
+    <dependency>
+      <groupId>org.apache.archiva</groupId>
+      <artifactId>metadata-repository-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.archiva</groupId>
+      <artifactId>metadata-repository-api</artifactId>
+      <classifier>tests</classifier>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.slf4j</groupId>
+      <artifactId>slf4j-simple</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.codehaus.plexus</groupId>
+      <artifactId>plexus-spring</artifactId>
+      <scope>test</scope>
+    </dependency>
+    <!-- TODO: dependency management -->
+    <dependency>
+      <groupId>javax.jcr</groupId>
+      <artifactId>jcr</artifactId>
+      <version>2.0</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.jackrabbit</groupId>
+      <artifactId>jackrabbit-core</artifactId>
+      <version>${jackrabbit.version}</version>
+      <!-- TODO: trim more, or look for a lighter container? -->
+      <exclusions>
+        <exclusion>
+          <groupId>commons-logging</groupId>
+          <artifactId>commons-logging</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.apache.derby</groupId>
+          <artifactId>derby</artifactId>
+        </exclusion>
+        <exclusion>
+          <groupId>org.apache.jackrabbit</groupId>
+          <artifactId>jackrabbit-text-extractors</artifactId>
+        </exclusion>
+      </exclusions>
+    </dependency>
+  </dependencies>
+</project>

Added: archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepository.java
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepository.java?rev=1050283&view=auto
==============================================================================
--- archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepository.java (added)
+++ archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepository.java Fri Dec 17 06:26:09 2010
@@ -0,0 +1,1245 @@
+package org.apache.archiva.metadata.repository.jcr;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.metadata.model.ArtifactMetadata;
+import org.apache.archiva.metadata.model.CiManagement;
+import org.apache.archiva.metadata.model.Dependency;
+import org.apache.archiva.metadata.model.IssueManagement;
+import org.apache.archiva.metadata.model.License;
+import org.apache.archiva.metadata.model.MailingList;
+import org.apache.archiva.metadata.model.MetadataFacet;
+import org.apache.archiva.metadata.model.MetadataFacetFactory;
+import org.apache.archiva.metadata.model.Organization;
+import org.apache.archiva.metadata.model.ProjectMetadata;
+import org.apache.archiva.metadata.model.ProjectVersionMetadata;
+import org.apache.archiva.metadata.model.ProjectVersionReference;
+import org.apache.archiva.metadata.model.Scm;
+import org.apache.archiva.metadata.repository.MetadataRepository;
+import org.apache.archiva.metadata.repository.MetadataResolutionException;
+import org.apache.jackrabbit.core.TransientRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import javax.jcr.LoginException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.PathNotFoundException;
+import javax.jcr.Property;
+import javax.jcr.PropertyIterator;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.SimpleCredentials;
+
+/**
+ * @plexus.component role="org.apache.archiva.metadata.repository.MetadataRepository"
+ * @todo path construction should be centralised
+ * @todo review all methods for alternate implementations (use of queries)
+ * @todo below: exception handling
+ * @todo below: revise storage format for project version metadata
+ */
+public class JcrMetadataRepository
+    implements MetadataRepository
+{
+    /**
+     * @plexus.requirement role="org.apache.archiva.metadata.model.MetadataFacetFactory"
+     */
+    private Map<String, MetadataFacetFactory> metadataFacetFactories;
+
+    private static final Logger log = LoggerFactory.getLogger( JcrMetadataRepository.class );
+
+    private static Repository repository;
+
+    private Session session;
+
+    public JcrMetadataRepository()
+    {
+        // TODO: need to close this at the end - do we need to add it in the API?
+
+        try
+        {
+            // TODO: push this in from the test, and make it possible from the webapp
+            if ( repository == null )
+            {
+                repository = new TransientRepository( new File( "src/test/repository.xml" ), new File( "target/jcr" ) );
+            }
+            // TODO: shouldn't do this in constructor since it's a singleton
+            session = repository.login( new SimpleCredentials( "username", "password".toCharArray() ) );
+        }
+        catch ( LoginException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+    }
+
+    public void updateProject( String repositoryId, ProjectMetadata project )
+    {
+        String namespace = project.getNamespace();
+        String projectId = project.getId();
+        updateProject( repositoryId, namespace, projectId );
+    }
+
+    private void updateProject( String repositoryId, String namespace, String projectId )
+    {
+        updateNamespace( repositoryId, namespace );
+
+        try
+        {
+            Node namespaceNode = getOrCreateNamespaceNode( repositoryId, namespace );
+            getOrCreateNode( namespaceNode, projectId );
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+    }
+
+    public void updateArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
+                                ArtifactMetadata artifactMeta )
+    {
+        try
+        {
+            Node node = getOrCreateArtifactNode( repositoryId, namespace, projectId, projectVersion,
+                                                 artifactMeta.getId() );
+
+            Calendar cal = Calendar.getInstance();
+            cal.setTime( artifactMeta.getFileLastModified() );
+            node.setProperty( "updated", cal );
+
+            cal = Calendar.getInstance();
+            cal.setTime( artifactMeta.getWhenGathered() );
+            node.setProperty( "whenGathered", cal );
+
+            node.setProperty( "size", artifactMeta.getSize() );
+            node.setProperty( "md5", artifactMeta.getMd5() );
+            node.setProperty( "sha1", artifactMeta.getSha1() );
+
+            node.setProperty( "version", artifactMeta.getVersion() );
+
+            // TODO: namespaced properties instead?
+            Node facetNode = getOrCreateNode( node, "facets" );
+            for ( MetadataFacet facet : artifactMeta.getFacetList() )
+            {
+                // TODO: need to clear it?
+                Node n = getOrCreateNode( facetNode, facet.getFacetId() );
+
+                for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
+                {
+                    n.setProperty( entry.getKey(), entry.getValue() );
+                }
+            }
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+    }
+
+    private Node getOrCreateArtifactNode( String repositoryId, String namespace, String projectId,
+                                          String projectVersion, String id )
+        throws RepositoryException
+    {
+        Node versionNode = getOrCreateProjectVersionNode( repositoryId, namespace, projectId, projectVersion );
+        return getOrCreateNode( versionNode, id );
+    }
+
+    public void updateProjectVersion( String repositoryId, String namespace, String projectId,
+                                      ProjectVersionMetadata versionMetadata )
+    {
+        updateProject( repositoryId, namespace, projectId );
+
+        try
+        {
+            Node versionNode = getOrCreateProjectVersionNode( repositoryId, namespace, projectId,
+                                                              versionMetadata.getId() );
+
+            versionNode.setProperty( "name", versionMetadata.getName() );
+            versionNode.setProperty( "description", versionMetadata.getDescription() );
+            versionNode.setProperty( "url", versionMetadata.getUrl() );
+            versionNode.setProperty( "incomplete", versionMetadata.isIncomplete() );
+
+            // TODO: decide how to treat these in the content repo
+            if ( versionMetadata.getScm() != null )
+            {
+                versionNode.setProperty( "scm.connection", versionMetadata.getScm().getConnection() );
+                versionNode.setProperty( "scm.developerConnection", versionMetadata.getScm().getDeveloperConnection() );
+                versionNode.setProperty( "scm.url", versionMetadata.getScm().getUrl() );
+            }
+            if ( versionMetadata.getCiManagement() != null )
+            {
+                versionNode.setProperty( "ci.system", versionMetadata.getCiManagement().getSystem() );
+                versionNode.setProperty( "ci.url", versionMetadata.getCiManagement().getUrl() );
+            }
+            if ( versionMetadata.getIssueManagement() != null )
+            {
+                versionNode.setProperty( "issue.system", versionMetadata.getIssueManagement().getSystem() );
+                versionNode.setProperty( "issue.url", versionMetadata.getIssueManagement().getUrl() );
+            }
+            if ( versionMetadata.getOrganization() != null )
+            {
+                versionNode.setProperty( "org.name", versionMetadata.getOrganization().getName() );
+                versionNode.setProperty( "org.url", versionMetadata.getOrganization().getUrl() );
+            }
+            int i = 0;
+            for ( License license : versionMetadata.getLicenses() )
+            {
+                versionNode.setProperty( "license." + i + ".name", license.getName() );
+                versionNode.setProperty( "license." + i + ".url", license.getUrl() );
+                i++;
+            }
+            i = 0;
+            for ( MailingList mailingList : versionMetadata.getMailingLists() )
+            {
+                versionNode.setProperty( "mailingList." + i + ".archive", mailingList.getMainArchiveUrl() );
+                versionNode.setProperty( "mailingList." + i + ".name", mailingList.getName() );
+                versionNode.setProperty( "mailingList." + i + ".post", mailingList.getPostAddress() );
+                versionNode.setProperty( "mailingList." + i + ".unsubscribe", mailingList.getUnsubscribeAddress() );
+                versionNode.setProperty( "mailingList." + i + ".subscribe", mailingList.getSubscribeAddress() );
+                versionNode.setProperty( "mailingList." + i + ".otherArchives", join(
+                    mailingList.getOtherArchives() ) );
+                i++;
+            }
+            i = 0;
+            for ( Dependency dependency : versionMetadata.getDependencies() )
+            {
+                versionNode.setProperty( "dependency." + i + ".classifier", dependency.getClassifier() );
+                versionNode.setProperty( "dependency." + i + ".scope", dependency.getScope() );
+                versionNode.setProperty( "dependency." + i + ".systemPath", dependency.getSystemPath() );
+                versionNode.setProperty( "dependency." + i + ".artifactId", dependency.getArtifactId() );
+                versionNode.setProperty( "dependency." + i + ".groupId", dependency.getGroupId() );
+                versionNode.setProperty( "dependency." + i + ".version", dependency.getVersion() );
+                versionNode.setProperty( "dependency." + i + ".type", dependency.getType() );
+                i++;
+            }
+
+            // TODO: namespaced properties instead?
+            Node facetNode = getOrCreateNode( versionNode, "facets" );
+            for ( MetadataFacet facet : versionMetadata.getFacetList() )
+            {
+                // TODO: shouldn't need to recreate, just update
+                if ( facetNode.hasNode( facet.getFacetId() ) )
+                {
+                    facetNode.getNode( facet.getFacetId() ).remove();
+                }
+                Node n = facetNode.addNode( facet.getFacetId() );
+                
+                for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
+                {
+                    n.setProperty( entry.getKey(), entry.getValue() );
+                }
+            }
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+    }
+
+    private Node getOrCreateProjectVersionNode( String repositoryId, String namespace, String projectId,
+                                                String projectVersion )
+        throws RepositoryException
+    {
+        Node namespaceNode = getOrCreateNamespaceNode( repositoryId, namespace );
+        Node projectNode = getOrCreateNode( namespaceNode, projectId );
+        return getOrCreateNode( projectNode, projectVersion );
+    }
+
+    private Node getOrCreateNode( Node baseNode, String name )
+        throws RepositoryException
+    {
+        return baseNode.hasNode( name ) ? baseNode.getNode( name ) : baseNode.addNode( name );
+    }
+
+    private Node getOrCreateNamespaceNode( String repositoryId, String namespace )
+        throws RepositoryException
+    {
+        Node repo = getOrCreateRepositoryContentNode( repositoryId );
+        return getOrCreateNode( repo, namespace );
+    }
+
+    private Node getOrCreateRepositoryContentNode( String repositoryId )
+        throws RepositoryException
+    {
+        Node node = getOrCreateRepositoryNode( repositoryId );
+        return getOrCreateNode( node, "content" );
+    }
+
+    private Node getOrCreateRepositoryNode( String repositoryId )
+        throws RepositoryException
+    {
+        Node root = session.getRootNode();
+        Node node = getOrCreateNode( root, "repositories" );
+        node = getOrCreateNode( node, repositoryId );
+        return node;
+    }
+
+    public void updateProjectReference( String repositoryId, String namespace, String projectId, String projectVersion,
+                                        ProjectVersionReference reference )
+    {
+        // TODO: try weak reference?
+        // TODO: is this tree the right way up? It differs from the content model
+        try
+        {
+            Node node = getOrCreateRepositoryContentNode( repositoryId );
+            node = getOrCreateNode( node, namespace );
+            node = getOrCreateNode( node, projectId );
+            node = getOrCreateNode( node, projectVersion );
+            node = getOrCreateNode( node, "references" );
+            node = getOrCreateNode( node, reference.getNamespace() );
+            node = getOrCreateNode( node, reference.getProjectId() );
+            node = getOrCreateNode( node, reference.getProjectVersion() );
+            node.setProperty( "type", reference.getReferenceType().toString() );
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+    }
+
+    public void updateNamespace( String repositoryId, String namespace )
+    {
+        try
+        {
+            Node node = getOrCreateNamespaceNode( repositoryId, namespace );
+            node.setProperty( "namespace", namespace );
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+    }
+
+    public List<String> getMetadataFacets( String repositoryId, String facetId )
+    {
+        List<String> facets = new ArrayList<String>();
+
+        try
+        {
+            Node root = session.getRootNode();
+            Node node = root.getNode( "repositories/" + repositoryId + "/facets/" + facetId );
+
+            // TODO: could we simply query all nodes with no children?
+            recurse( facets, "", node );
+        }
+        catch ( PathNotFoundException e )
+        {
+            // TODO: handle this case differently?
+            // currently ignored
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+        return facets;
+    }
+
+    private void recurse( List<String> facets, String prefix, Node node )
+        throws RepositoryException
+    {
+        NodeIterator iterator = node.getNodes();
+        while ( iterator.hasNext() )
+        {
+            Node n = iterator.nextNode();
+            String name = prefix + "/" + n.getName();
+            if ( n.hasNodes() )
+            {
+                recurse( facets, name, n );
+            }
+            else
+            {
+                // strip leading / first
+                facets.add( name.substring( 1 ) );
+            }
+        }
+    }
+
+
+    public MetadataFacet getMetadataFacet( String repositoryId, String facetId, String name )
+    {
+        MetadataFacet metadataFacet = null;
+        try
+        {
+            Node root = session.getRootNode();
+            Node node = root.getNode( "repositories/" + repositoryId + "/facets/" + facetId + "/" + name );
+
+            MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
+            if ( metadataFacetFactory != null )
+            {
+                metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
+                Map<String, String> map = new HashMap<String, String>();
+                PropertyIterator iterator = node.getProperties();
+                while ( iterator.hasNext() )
+                {
+                    Property property = iterator.nextProperty();
+                    String p = property.getName();
+                    if ( !p.startsWith( "jcr:" ) )
+                    {
+                        map.put( p, property.getString() );
+                    }
+                }
+                metadataFacet.fromProperties( map );
+            }
+        }
+        catch ( PathNotFoundException e )
+        {
+            // TODO: handle this case differently?
+            // currently ignored
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+        return metadataFacet;
+    }
+
+    public void addMetadataFacet( String repositoryId, MetadataFacet metadataFacet )
+    {
+        try
+        {
+            Node repo = getOrCreateRepositoryNode( repositoryId );
+            Node facets = getOrCreateNode( repo, "facets" );
+
+            String id = metadataFacet.getFacetId();
+            Node facetNode = getOrCreateNode( facets, id );
+
+            Node node = getOrCreatePath( facetNode, metadataFacet.getName() );
+
+            for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
+            {
+                node.setProperty( entry.getKey(), entry.getValue() );
+            }
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+    }
+
+    private Node getOrCreatePath( Node baseNode, String name )
+        throws RepositoryException
+    {
+        Node node = baseNode;
+        for ( String n : name.split( "/" ) )
+        {
+            node = getOrCreateNode( node, n );
+        }
+        return node;
+    }
+
+    public void removeMetadataFacets( String repositoryId, String facetId )
+    {
+        try
+        {
+            Node root = session.getRootNode();
+            String path = "repositories/" + repositoryId + "/facets/" + facetId;
+            // TODO: exception if missing?
+            if ( root.hasNode( path ) )
+            {
+                root.getNode( path ).remove();
+            }
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+    }
+
+    public void removeMetadataFacet( String repositoryId, String facetId, String name )
+    {
+        try
+        {
+            Node root = session.getRootNode();
+            String path = "repositories/" + repositoryId + "/facets/" + facetId + "/" + name;
+            // TODO: exception if missing?
+            if ( root.hasNode( path ) )
+            {
+                Node node = root.getNode( path );
+                do
+                {
+                    Node parent = node.getParent();
+                    node.remove();
+                    node = parent;
+                }
+                while ( !node.hasNodes() );
+            }
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+    }
+
+    public List<ArtifactMetadata> getArtifactsByDateRange( String repoId, Date startTime, Date endTime )
+    {
+        // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
+        //  of this information (eg. in Lucene, as before)
+
+        List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
+        for ( String ns : getRootNamespaces( repoId ) )
+        {
+            getArtifactsByDateRange( artifacts, repoId, ns, startTime, endTime );
+        }
+        Collections.sort( artifacts, new ArtifactComparator() );
+        return artifacts;
+    }
+
+    private void getArtifactsByDateRange( List<ArtifactMetadata> artifacts, String repoId, String ns, Date startTime,
+                                          Date endTime )
+    {
+        for ( String namespace : getNamespaces( repoId, ns ) )
+        {
+            getArtifactsByDateRange( artifacts, repoId, ns + "." + namespace, startTime, endTime );
+        }
+
+        for ( String project : getProjects( repoId, ns ) )
+        {
+            for ( String version : getProjectVersions( repoId, ns, project ) )
+            {
+                for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) )
+                {
+                    if ( startTime == null || startTime.before( artifact.getWhenGathered() ) )
+                    {
+                        if ( endTime == null || endTime.after( artifact.getWhenGathered() ) )
+                        {
+                            artifacts.add( artifact );
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    public Collection<String> getRepositories()
+    {
+        List<String> repositories;
+
+        try
+        {
+            Node root = session.getRootNode();
+            if ( root.hasNode( "repositories" ) )
+            {
+                Node node = root.getNode( "repositories" );
+
+                repositories = new ArrayList<String>();
+                NodeIterator i = node.getNodes();
+                while ( i.hasNext() )
+                {
+                    Node n = i.nextNode();
+                    repositories.add( n.getName() );
+                }
+            }
+            else
+            {
+                repositories = Collections.emptyList();
+            }
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+        return repositories;
+    }
+
+    public List<ArtifactMetadata> getArtifactsByChecksum( String repositoryId, String checksum )
+    {
+        // TODO: this is quite slow - if we are to persist with this repository implementation we should build an index
+        //  of this information (eg. in Lucene, as before)
+        // alternatively, we could build a referential tree in the content repository, however it would need some levels
+        // of depth to avoid being too broad to be useful (eg. /repository/checksums/a/ab/abcdef1234567)
+
+        List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
+        for ( String ns : getRootNamespaces( repositoryId ) )
+        {
+            getArtifactsByChecksum( artifacts, repositoryId, ns, checksum );
+        }
+        return artifacts;
+    }
+
+    private void getArtifactsByChecksum( List<ArtifactMetadata> artifacts, String repositoryId, String ns,
+                                         String checksum )
+    {
+        for ( String namespace : getNamespaces( repositoryId, ns ) )
+        {
+            getArtifactsByChecksum( artifacts, repositoryId, ns + "." + namespace, checksum );
+        }
+
+        for ( String project : getProjects( repositoryId, ns ) )
+        {
+            for ( String version : getProjectVersions( repositoryId, ns, project ) )
+            {
+                for ( ArtifactMetadata artifact : getArtifacts( repositoryId, ns, project, version ) )
+                {
+                    if ( checksum.equals( artifact.getMd5() ) || checksum.equals( artifact.getSha1() ) )
+                    {
+                        artifacts.add( artifact );
+                    }
+                }
+            }
+        }
+    }
+
+    public void deleteArtifact( String repositoryId, String namespace, String projectId, String projectVersion,
+                                String id )
+    {
+        try
+        {
+            Node root = session.getRootNode();
+            String path =
+                "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion +
+                    "/" + id;
+            // TODO: exception if missing?
+            if ( root.hasNode( path ) )
+            {
+                root.getNode( path ).remove();
+            }
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+    }
+
+    public void deleteRepository( String repositoryId )
+    {
+        try
+        {
+            Node root = session.getRootNode();
+            String path = "repositories/" + repositoryId;
+            // TODO: exception if missing?
+            if ( root.hasNode( path ) )
+            {
+                root.getNode( path ).remove();
+            }
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+    }
+
+    public List<ArtifactMetadata> getArtifacts( String repositoryId )
+    {
+        // TODO: query faster?
+        List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
+        for ( String ns : getRootNamespaces( repositoryId ) )
+        {
+            getArtifacts( artifacts, repositoryId, ns );
+        }
+        return artifacts;
+    }
+
+    private void getArtifacts( List<ArtifactMetadata> artifacts, String repoId, String ns )
+    {
+        for ( String namespace : getNamespaces( repoId, ns ) )
+        {
+            getArtifacts( artifacts, repoId, ns + "." + namespace );
+        }
+
+        for ( String project : getProjects( repoId, ns ) )
+        {
+            for ( String version : getProjectVersions( repoId, ns, project ) )
+            {
+                for ( ArtifactMetadata artifact : getArtifacts( repoId, ns, project, version ) )
+                {
+                    artifacts.add( artifact );
+                }
+            }
+        }
+    }
+
+    public ProjectMetadata getProject( String repositoryId, String namespace, String projectId )
+    {
+        ProjectMetadata metadata = null;
+
+        try
+        {
+            Node root = session.getRootNode();
+
+            // basically just checking it exists
+            String path = "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId;
+            if ( root.hasNode( path ) )
+            {
+                metadata = new ProjectMetadata();
+                metadata.setId( projectId );
+                metadata.setNamespace( namespace );
+            }
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+
+        return metadata;
+    }
+
+    public ProjectVersionMetadata getProjectVersion( String repositoryId, String namespace, String projectId,
+                                                     String projectVersion )
+        throws MetadataResolutionException
+    {
+        ProjectVersionMetadata versionMetadata;
+
+        try
+        {
+            Node root = session.getRootNode();
+
+            String path =
+                "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion;
+            if ( !root.hasNode( path ) )
+            {
+                return null;
+            }
+
+            Node node = root.getNode( path );
+
+            versionMetadata = new ProjectVersionMetadata();
+            versionMetadata.setId( projectVersion );
+            versionMetadata.setName( getPropertyString( node, "name" ) );
+            versionMetadata.setDescription( getPropertyString( node, "description" ) );
+            versionMetadata.setUrl( getPropertyString( node, "url" ) );
+            versionMetadata.setIncomplete( node.hasProperty( "incomplete" ) && node.getProperty(
+                "incomplete" ).getBoolean() );
+
+            // TODO: decide how to treat these in the content repo
+            String scmConnection = getPropertyString( node, "scm.connection" );
+            String scmDeveloperConnection = getPropertyString( node, "scm.developerConnection" );
+            String scmUrl = getPropertyString( node, "scm.url" );
+            if ( scmConnection != null || scmDeveloperConnection != null || scmUrl != null )
+            {
+                Scm scm = new Scm();
+                scm.setConnection( scmConnection );
+                scm.setDeveloperConnection( scmDeveloperConnection );
+                scm.setUrl( scmUrl );
+                versionMetadata.setScm( scm );
+            }
+
+            String ciSystem = getPropertyString( node, "ci.system" );
+            String ciUrl = getPropertyString( node, "ci.url" );
+            if ( ciSystem != null || ciUrl != null )
+            {
+                CiManagement ci = new CiManagement();
+                ci.setSystem( ciSystem );
+                ci.setUrl( ciUrl );
+                versionMetadata.setCiManagement( ci );
+            }
+
+            String issueSystem = getPropertyString( node, "issue.system" );
+            String issueUrl = getPropertyString( node, "issue.url" );
+            if ( issueSystem != null || issueUrl != null )
+            {
+                IssueManagement issueManagement = new IssueManagement();
+                issueManagement.setSystem( issueSystem );
+                issueManagement.setUrl( issueUrl );
+                versionMetadata.setIssueManagement( issueManagement );
+            }
+
+            String orgName = getPropertyString( node, "org.name" );
+            String orgUrl = getPropertyString( node, "org.url" );
+            if ( orgName != null || orgUrl != null )
+            {
+                Organization org = new Organization();
+                org.setName( orgName );
+                org.setUrl( orgUrl );
+                versionMetadata.setOrganization( org );
+            }
+
+            boolean done = false;
+            int i = 0;
+            while ( !done )
+            {
+                String licenseName = getPropertyString( node, "license." + i + ".name" );
+                String licenseUrl = getPropertyString( node, "license." + i + ".url" );
+                if ( licenseName != null || licenseUrl != null )
+                {
+                    License license = new License();
+                    license.setName( licenseName );
+                    license.setUrl( licenseUrl );
+                    versionMetadata.addLicense( license );
+                }
+                else
+                {
+                    done = true;
+                }
+                i++;
+            }
+
+            done = false;
+            i = 0;
+            while ( !done )
+            {
+                String mailingListName = getPropertyString( node, "mailingList." + i + ".name" );
+                if ( mailingListName != null )
+                {
+                    MailingList mailingList = new MailingList();
+                    mailingList.setName( mailingListName );
+                    mailingList.setMainArchiveUrl( getPropertyString( node, "mailingList." + i + ".archive" ) );
+                    String n = "mailingList." + i + ".otherArchives";
+                    if ( node.hasProperty( n ) )
+                    {
+                        mailingList.setOtherArchives( Arrays.asList( getPropertyString( node, n ).split( "," ) ) );
+                    }
+                    else
+                    {
+                        mailingList.setOtherArchives( Collections.<String>emptyList() );
+                    }
+                    mailingList.setPostAddress( getPropertyString( node, "mailingList." + i + ".post" ) );
+                    mailingList.setSubscribeAddress( getPropertyString( node, "mailingList." + i + ".subscribe" ) );
+                    mailingList.setUnsubscribeAddress( getPropertyString( node, "mailingList." + i + ".unsubscribe" ) );
+                    versionMetadata.addMailingList( mailingList );
+                }
+                else
+                {
+                    done = true;
+                }
+                i++;
+            }
+
+            done = false;
+            i = 0;
+            while ( !done )
+            {
+                String dependencyArtifactId = getPropertyString( node, "dependency." + i + ".artifactId" );
+                if ( dependencyArtifactId != null )
+                {
+                    Dependency dependency = new Dependency();
+                    dependency.setArtifactId( dependencyArtifactId );
+                    dependency.setGroupId( getPropertyString( node, "dependency." + i + ".groupId" ) );
+                    dependency.setClassifier( getPropertyString( node, "dependency." + i + ".classifier" ) );
+                    dependency.setOptional( Boolean.valueOf( getPropertyString( node,
+                                                                                "dependency." + i + ".optional" ) ) );
+                    dependency.setScope( getPropertyString( node, "dependency." + i + ".scope" ) );
+                    dependency.setSystemPath( getPropertyString( node, "dependency." + i + ".systemPath" ) );
+                    dependency.setType( getPropertyString( node, "dependency." + i + ".type" ) );
+                    dependency.setVersion( getPropertyString( node, "dependency." + i + ".version" ) );
+                    versionMetadata.addDependency( dependency );
+                }
+                else
+                {
+                    done = true;
+                }
+                i++;
+            }
+
+            if ( node.hasNode( "facets" ) )
+            {
+                NodeIterator j = node.getNode( "facets" ).getNodes();
+
+                while ( j.hasNext() )
+                {
+                    Node facetNode = j.nextNode();
+
+                    MetadataFacetFactory factory = metadataFacetFactories.get( facetNode.getName() );
+                    if ( factory == null )
+                    {
+                        log.error( "Attempted to load unknown project version metadata facet: " + facetNode.getName() );
+                    }
+                    else
+                    {
+                        MetadataFacet facet = factory.createMetadataFacet();
+                        Map<String, String> map = new HashMap<String, String>();
+                        PropertyIterator iterator = facetNode.getProperties();
+                        while ( iterator.hasNext() )
+                        {
+                            Property property = iterator.nextProperty();
+                            String p = property.getName();
+                            if ( !p.startsWith( "jcr:" ) )
+                            {
+                                map.put( p, property.getString() );
+                            }
+                        }
+                        facet.fromProperties( map );
+                        versionMetadata.addFacet( facet );
+                    }
+                }
+            }
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+
+        return versionMetadata;
+    }
+
+    private static String getPropertyString( Node node, String name )
+        throws RepositoryException
+    {
+        return node.hasProperty( name ) ? node.getProperty( name ).getString() : null;
+    }
+
+    public Collection<String> getArtifactVersions( String repositoryId, String namespace, String projectId,
+                                                   String projectVersion )
+    {
+        Set<String> versions = new LinkedHashSet<String>();
+
+        try
+        {
+            Node root = session.getRootNode();
+
+            Node node = root.getNode(
+                "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion );
+
+            NodeIterator iterator = node.getNodes();
+            while ( iterator.hasNext() )
+            {
+                Node n = iterator.nextNode();
+
+                versions.add( n.getProperty( "version" ).getString() );
+            }
+        }
+        catch ( PathNotFoundException e )
+        {
+            // ignore repo not found for now
+            // TODO: throw specific exception if repo doesn't exist
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+
+        return versions;
+    }
+
+    public Collection<ProjectVersionReference> getProjectReferences( String repositoryId, String namespace,
+                                                                     String projectId, String projectVersion )
+    {
+        List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
+
+        try
+        {
+            Node root = session.getRootNode();
+
+            String path =
+                "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion +
+                    "/references";
+            if ( root.hasNode( path ) )
+            {
+                Node node = root.getNode( path );
+
+                // TODO: use query by reference type
+                NodeIterator i = node.getNodes();
+                while ( i.hasNext() )
+                {
+                    Node ns = i.nextNode();
+
+                    NodeIterator j = ns.getNodes();
+
+                    while ( j.hasNext() )
+                    {
+                        Node project = j.nextNode();
+
+                        NodeIterator k = project.getNodes();
+
+                        while ( k.hasNext() )
+                        {
+                            Node version = k.nextNode();
+
+                            ProjectVersionReference ref = new ProjectVersionReference();
+                            ref.setNamespace( ns.getName() );
+                            ref.setProjectId( project.getName() );
+                            ref.setProjectVersion( version.getName() );
+                            String type = version.getProperty( "type" ).getString();
+                            ref.setReferenceType( ProjectVersionReference.ReferenceType.valueOf( type ) );
+                            references.add( ref );
+                        }
+                    }
+                }
+            }
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+
+        return references;
+    }
+
+    public Collection<String> getRootNamespaces( String repositoryId )
+    {
+        return getNamespaces( repositoryId, null );
+    }
+
+    private Collection<String> getNodeNames( String path )
+    {
+        List<String> names = new ArrayList<String>();
+
+        try
+        {
+            Node root = session.getRootNode();
+
+            Node repository = root.getNode( path );
+
+            NodeIterator nodes = repository.getNodes();
+            while ( nodes.hasNext() )
+            {
+                Node node = nodes.nextNode();
+                names.add( node.getName() );
+            }
+        }
+        catch ( PathNotFoundException e )
+        {
+            // ignore repo not found for now
+            // TODO: throw specific exception if repo doesn't exist
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+
+        return names;
+    }
+
+    public Collection<String> getNamespaces( String repositoryId, String baseNamespace )
+    {
+        // TODO: could be simpler with pathed namespaces, rely on namespace property
+        Collection<String> allNamespaces = getNodeNames( "repositories/" + repositoryId + "/content" );
+
+        Set<String> namespaces = new LinkedHashSet<String>();
+        int fromIndex = baseNamespace != null ? baseNamespace.length() + 1 : 0;
+        for ( String namespace : allNamespaces )
+        {
+            if ( baseNamespace == null || namespace.startsWith( baseNamespace + "." ) )
+            {
+                int i = namespace.indexOf( '.', fromIndex );
+                if ( i >= 0 )
+                {
+                    namespaces.add( namespace.substring( fromIndex, i ) );
+                }
+                else
+                {
+                    namespaces.add( namespace.substring( fromIndex ) );
+                }
+            }
+        }
+        return new ArrayList<String>( namespaces );
+    }
+
+    public Collection<String> getProjects( String repositoryId, String namespace )
+    {
+        // TODO: could be simpler with pathed namespaces, rely on namespace property
+        return getNodeNames( "repositories/" + repositoryId + "/content/" + namespace );
+    }
+
+    public Collection<String> getProjectVersions( String repositoryId, String namespace, String projectId )
+    {
+        // TODO: could be simpler with pathed namespaces, rely on namespace property
+        return getNodeNames( "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId );
+    }
+
+    public Collection<ArtifactMetadata> getArtifacts( String repositoryId, String namespace, String projectId,
+                                                      String projectVersion )
+    {
+        List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>();
+
+        try
+        {
+            Node root = session.getRootNode();
+            String path =
+                "repositories/" + repositoryId + "/content/" + namespace + "/" + projectId + "/" + projectVersion;
+
+            if ( root.hasNode( path ) )
+            {
+                Node node = root.getNode( path );
+
+                NodeIterator iterator = node.getNodes();
+                while ( iterator.hasNext() )
+                {
+                    Node artifactNode = iterator.nextNode();
+
+                    String id = artifactNode.getName();
+
+                    ArtifactMetadata artifact = new ArtifactMetadata();
+                    artifact.setId( id );
+                    artifact.setRepositoryId( repositoryId );
+                    artifact.setNamespace( namespace );
+                    artifact.setProject( projectId );
+                    artifact.setProjectVersion( projectVersion );
+                    artifact.setVersion( artifactNode.hasProperty( "version" ) ? artifactNode.getProperty(
+                        "version" ).getString() : projectVersion );
+
+                    if ( artifactNode.hasProperty( "updated" ) )
+                    {
+                        artifact.setFileLastModified( artifactNode.getProperty(
+                            "updated" ).getDate().getTimeInMillis() );
+                    }
+
+                    if ( artifactNode.hasProperty( "whenGathered" ) )
+                    {
+                        artifact.setWhenGathered( artifactNode.getProperty( "whenGathered" ).getDate().getTime() );
+                    }
+
+                    if ( artifactNode.hasProperty( "size" ) )
+                    {
+                        artifact.setSize( artifactNode.getProperty( "size" ).getLong() );
+                    }
+
+                    if ( artifactNode.hasProperty( "md5" ) )
+                    {
+                        artifact.setMd5( artifactNode.getProperty( "md5" ).getString() );
+                    }
+
+                    if ( artifactNode.hasProperty( "sha1" ) )
+                    {
+                        artifact.setSha1( artifactNode.getProperty( "sha1" ).getString() );
+                    }
+
+                    if ( artifactNode.hasNode( "facets" ) )
+                    {
+                        NodeIterator j = artifactNode.getNode( "facets" ).getNodes();
+
+                        while ( j.hasNext() )
+                        {
+                            Node facetNode = j.nextNode();
+
+                            MetadataFacetFactory factory = metadataFacetFactories.get( facetNode.getName() );
+                            if ( factory == null )
+                            {
+                                log.error( "Attempted to load unknown project version metadata facet: " + facetNode.getName() );
+                            }
+                            else
+                            {
+                                MetadataFacet facet = factory.createMetadataFacet();
+                                Map<String, String> map = new HashMap<String, String>();
+                                PropertyIterator i = facetNode.getProperties();
+                                while ( i.hasNext() )
+                                {
+                                    Property p = i.nextProperty();
+                                    String property = p.getName();
+                                    map.put( property, p.getString() );
+                                }
+                                facet.fromProperties( map );
+                                artifact.addFacet( facet );
+                            }
+                        }
+                    }
+                    artifacts.add( artifact );
+                }
+            }
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+
+        return artifacts;
+    }
+
+    void close()
+    {
+        try
+        {
+            // TODO: this shouldn't be here! Repository may need a context
+            session.save();
+        }
+        catch ( RepositoryException e )
+        {
+            // TODO
+            throw new RuntimeException( e );
+        }
+        session.logout();
+    }
+
+    public void setMetadataFacetFactories( Map<String, MetadataFacetFactory> metadataFacetFactories )
+    {
+        this.metadataFacetFactories = metadataFacetFactories;
+
+        // TODO: check if actually called by normal injection
+
+//        for ( String facetId : metadataFacetFactories.keySet() )
+//        {
+//            // TODO: second arg should be a better URL for the namespace
+//            session.getWorkspace().getNamespaceRegistry().registerNamespace( facetId, facetId );
+//        }
+    }
+
+    private static class ArtifactComparator
+        implements Comparator<ArtifactMetadata>
+    {
+        public int compare( ArtifactMetadata artifact1, ArtifactMetadata artifact2 )
+        {
+            if ( artifact1.getWhenGathered() == artifact2.getWhenGathered() )
+            {
+                return 0;
+            }
+            if ( artifact1.getWhenGathered() == null )
+            {
+                return 1;
+            }
+            if ( artifact2.getWhenGathered() == null )
+            {
+                return -1;
+            }
+            return artifact1.getWhenGathered().compareTo( artifact2.getWhenGathered() );
+        }
+    }
+
+    private String join( Collection<String> ids )
+    {
+        if ( ids != null && !ids.isEmpty() )
+        {
+            StringBuilder s = new StringBuilder();
+            for ( String id : ids )
+            {
+                s.append( id );
+                s.append( "," );
+            }
+            return s.substring( 0, s.length() - 1 );
+        }
+        return null;
+    }
+}

Added: archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepositoryTest.java
URL: http://svn.apache.org/viewvc/archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepositoryTest.java?rev=1050283&view=auto
==============================================================================
--- archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepositoryTest.java (added)
+++ archiva/trunk/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepositoryTest.java Fri Dec 17 06:26:09 2010
@@ -0,0 +1,58 @@
+package org.apache.archiva.metadata.repository.jcr;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.archiva.metadata.model.MetadataFacetFactory;
+import org.apache.archiva.metadata.repository.AbstractMetadataRepositoryTest;
+import org.apache.commons.io.FileUtils;
+
+import java.io.File;
+import java.util.Map;
+
+public class JcrMetadataRepositoryTest
+    extends AbstractMetadataRepositoryTest
+{
+    private JcrMetadataRepository jcrMetadataRepository;
+
+    public void setUp()
+        throws Exception
+    {
+        super.setUp();
+
+        File directory = getTestFile( "target/test-repositories" );
+        FileUtils.deleteDirectory( directory );
+
+        Map<String, MetadataFacetFactory> factories = createTestMetadataFacetFactories();
+
+        jcrMetadataRepository = new JcrMetadataRepository();
+        jcrMetadataRepository.setMetadataFacetFactories( factories );
+
+        this.repository = jcrMetadataRepository;
+    }
+
+    @Override
+    protected void tearDown()
+        throws Exception
+    {
+        super.tearDown();
+
+        jcrMetadataRepository.close();
+    }
+}