You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@archiva.apache.org by ma...@apache.org on 2019/08/19 22:08:16 UTC

[archiva] 01/02: Changing facet factories. Adding stream methods to metadata repository.

This is an automated email from the ASF dual-hosted git repository.

martin_s pushed a commit to branch feature/storage_refactoring
in repository https://gitbox.apache.org/repos/asf/archiva.git

commit 6cf4073a9e73b6517c51dc22db75fd97d2523695
Author: Martin Stockhammer <ma...@apache.org>
AuthorDate: Sun Aug 18 20:56:46 2019 +0200

    Changing facet factories. Adding stream methods to metadata repository.
---
 .../storage/maven2/MavenArtifactFacetFactory.java  |  12 +-
 .../storage/maven2/MavenProjectFacetFactory.java   |  12 +-
 .../metadata/model/MetadataFacetFactory.java       |  14 +-
 .../model/facets/AbstractMetadataFacetFactory.java |  67 ++++++
 .../metadata/repository/MetadataRepository.java    | 159 ++++++++++++-
 .../metadata/repository/MetadataResolver.java      |   8 +
 .../metadata/repository/MetadataService.java       | 100 ++++++++
 .../repository/AbstractMetadataRepositoryTest.java |  68 ++++--
 .../apache/archiva/audit/AuditEventFactory.java    |  24 +-
 .../generic/GenericMetadataFacetFactory.java       |  13 +-
 .../repository/jcr/JcrMetadataRepository.java      | 265 ++++++++++++++-------
 .../jcr/JcrRepositorySessionFactory.java           |  52 ++--
 .../repository/jcr/OakRepositoryFactory.java       |   4 +-
 .../archiva/metadata/repository/jcr/jcr-schema.cnd |   3 +-
 .../repository/jcr/JcrMetadataRepositoryTest.java  |  15 +-
 .../JcrRepositoryStatisticsGatheringTest.java      |   8 +-
 .../reports/RepositoryProblemFacetFactory.java     |  14 +-
 .../stats/RepositoryStatisticsFactory.java         |  15 +-
 18 files changed, 662 insertions(+), 191 deletions(-)

diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/MavenArtifactFacetFactory.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/MavenArtifactFacetFactory.java
index e0c57ba..29bd3f6 100644
--- a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/MavenArtifactFacetFactory.java
+++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/MavenArtifactFacetFactory.java
@@ -19,8 +19,8 @@ package org.apache.archiva.metadata.repository.storage.maven2;
  * under the License.
  */
 
-import org.apache.archiva.metadata.model.MetadataFacet;
 import org.apache.archiva.metadata.model.MetadataFacetFactory;
+import org.apache.archiva.metadata.model.facets.AbstractMetadataFacetFactory;
 import org.apache.archiva.metadata.model.maven2.MavenArtifactFacet;
 import org.springframework.stereotype.Service;
 
@@ -29,16 +29,20 @@ import org.springframework.stereotype.Service;
  */
 @Service("metadataFacetFactory#org.apache.archiva.metadata.repository.storage.maven2.artifact")
 public class MavenArtifactFacetFactory
-    implements MetadataFacetFactory
+    extends AbstractMetadataFacetFactory<MavenArtifactFacet>
 {
+    public MavenArtifactFacetFactory() {
+        super( MavenArtifactFacet.class);
+    }
+
     @Override
-    public MetadataFacet createMetadataFacet()
+    public MavenArtifactFacet createMetadataFacet()
     {
         return new MavenArtifactFacet();
     }
 
     @Override
-    public MetadataFacet createMetadataFacet( String repositoryId, String name )
+    public MavenArtifactFacet createMetadataFacet( String repositoryId, String name )
     {
         throw new UnsupportedOperationException( "There is no valid name for artifact facets" );
     }
diff --git a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/MavenProjectFacetFactory.java b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/MavenProjectFacetFactory.java
index a7bd89a..a3b68c6 100644
--- a/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/MavenProjectFacetFactory.java
+++ b/archiva-modules/archiva-maven/archiva-maven-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/MavenProjectFacetFactory.java
@@ -19,8 +19,8 @@ package org.apache.archiva.metadata.repository.storage.maven2;
  * under the License.
  */
 
-import org.apache.archiva.metadata.model.MetadataFacet;
 import org.apache.archiva.metadata.model.MetadataFacetFactory;
+import org.apache.archiva.metadata.model.facets.AbstractMetadataFacetFactory;
 import org.springframework.stereotype.Service;
 
 /**
@@ -28,16 +28,20 @@ import org.springframework.stereotype.Service;
  */
 @Service( "metadataFacetFactory#org.apache.archiva.metadata.repository.storage.maven2.project" )
 public class MavenProjectFacetFactory
-    implements MetadataFacetFactory
+    extends AbstractMetadataFacetFactory<MavenProjectFacet>
 {
+    public MavenProjectFacetFactory() {
+        super( MavenProjectFacet.class );
+    }
+
     @Override
-    public MetadataFacet createMetadataFacet()
+    public MavenProjectFacet createMetadataFacet()
     {
         return new MavenProjectFacet();
     }
 
     @Override
-    public MetadataFacet createMetadataFacet( String repositoryId, String name )
+    public MavenProjectFacet createMetadataFacet( String repositoryId, String name )
     {
         throw new UnsupportedOperationException( "There is no valid name for project version facets" );
     }
diff --git a/archiva-modules/metadata/metadata-model/src/main/java/org/apache/archiva/metadata/model/MetadataFacetFactory.java b/archiva-modules/metadata/metadata-model/src/main/java/org/apache/archiva/metadata/model/MetadataFacetFactory.java
index e0804ed..ec35ad7 100644
--- a/archiva-modules/metadata/metadata-model/src/main/java/org/apache/archiva/metadata/model/MetadataFacetFactory.java
+++ b/archiva-modules/metadata/metadata-model/src/main/java/org/apache/archiva/metadata/model/MetadataFacetFactory.java
@@ -19,9 +19,17 @@ package org.apache.archiva.metadata.model;
  * under the License.
  */
 
-public interface MetadataFacetFactory
+public interface MetadataFacetFactory<T extends MetadataFacet>
 {
-    MetadataFacet createMetadataFacet();
+    T createMetadataFacet();
 
-    MetadataFacet createMetadataFacet( String repositoryId, String name );
+    T createMetadataFacet( String repositoryId, String name );
+
+    default boolean assignsFacet( Class<?> clazz ) {
+        return getFacetClass( ).isAssignableFrom( clazz );
+    }
+
+    Class<T> getFacetClass( );
+
+    String getFacetId();
 }
diff --git a/archiva-modules/metadata/metadata-model/src/main/java/org/apache/archiva/metadata/model/facets/AbstractMetadataFacetFactory.java b/archiva-modules/metadata/metadata-model/src/main/java/org/apache/archiva/metadata/model/facets/AbstractMetadataFacetFactory.java
new file mode 100644
index 0000000..725774e
--- /dev/null
+++ b/archiva-modules/metadata/metadata-model/src/main/java/org/apache/archiva/metadata/model/facets/AbstractMetadataFacetFactory.java
@@ -0,0 +1,67 @@
+package org.apache.archiva.metadata.model.facets;
+
+/*
+ * 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.MetadataFacet;
+import org.apache.archiva.metadata.model.MetadataFacetFactory;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+public abstract class AbstractMetadataFacetFactory<T extends MetadataFacet> implements MetadataFacetFactory<T>
+{
+    private final String facetId;
+    private final Class<T> facetClazz;
+
+    protected AbstractMetadataFacetFactory( Class<T> facetClazz, String facetId) {
+        this.facetId = facetId;
+        this.facetClazz = facetClazz;
+    }
+
+    protected AbstractMetadataFacetFactory(Class<T> facetClazz ) {
+        this.facetClazz = facetClazz;
+        try
+        {
+            this.facetId = (String) this.facetClazz.getField( "FACET_ID" ).get(null);
+        }
+        catch ( Throwable e)
+        {
+            throw new RuntimeException( "There is no FACET_ID static public field on the class " + facetClazz );
+        }
+    }
+
+    @Override
+    public abstract T createMetadataFacet( );
+
+    @Override
+    public abstract T createMetadataFacet( String repositoryId, String name );
+
+    @Override
+    public Class<T> getFacetClass( )
+    {
+        return facetClazz;
+    }
+
+    @Override
+    public String getFacetId( )
+    {
+        return facetId;
+    }
+}
diff --git a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataRepository.java b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataRepository.java
index 5048353..c35d5f9 100644
--- a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataRepository.java
+++ b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataRepository.java
@@ -24,61 +24,187 @@ import org.apache.archiva.metadata.model.MetadataFacet;
 import org.apache.archiva.metadata.model.ProjectMetadata;
 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
 import org.apache.archiva.metadata.model.ProjectVersionReference;
+import org.apache.maven.index_shaded.lucene.util.packed.DirectMonotonicReader;
 
+import java.time.LocalDateTime;
+import java.time.ZonedDateTime;
 import java.util.Collection;
 import java.util.Date;
 import java.util.List;
+import java.util.stream.Stream;
 
+/**
+ * A Metadata repository provides information about artifact metadata. It does not provide the artifact data itself.
+ * It may be possible to use the same backend for metadata and storage, but this depends on the backends and they are
+ * provided by different APIs.
+ *
+ * The motivation for this API is to provide fast access to the repository metadata and fulltext search. Also dependencies
+ * are stored in this repository.
+ *
+ * The methods here do not update the artifacts itself. They are only updating the data in the metadata repository.
+ * That means, if you want to update some artifact, you should make sure to update the artifact itself and the metadata
+ * repository (either directly or by repository scanning).
+ *
+ * Currently we are providing JCR, File based and Cassandra as backend for the metadata.
+ *
+ * The metadata repository uses sessions for accessing the data. Please make sure to always close the sessions after using it.
+ * Best idiom for using the sessions:
+ * <code>
+ * try(RepositorySession session = sessionFactory.createSession() {
+ *     // do your stuff
+ * }
+ * </code>
+ *
+ * It is implementation dependent, if the sessions are really used by the backend. E.g. the file based implementation ignores
+ * the sessions completely.
+ *
+ * Sessions should be closed immediately after usage. If it is expensive to open a session for a given backend. The backend
+ * should provide a session pool if possible. There are methods for refreshing a session if needed.
+ *
+ * You should avoid stacking sessions, that means, do not create a new session in the same thread, when a session is opened already.
+ *
+ * Some backend implementations (JCR) update the metadata in the background, that means update of the metadata is not reflected
+ * immediately.
+ *
+ * The base metadata coordinates are:
+ * <ul>
+ *     <li>Repository ID: The identifier of the repository, where the artifact resides</li>
+ *     <li>Namespace: This is a hierarchical coordinate for locating the projects. E.g. this corresponds to the groupId in maven. </li>
+ *     <li>Project ID: The project itself</li>
+ *     <li>Version: Each project may have different versions.</li>
+ *     <li>Artifact: Artifacts correspond to files / blob data. Each artifact has additional metadata, like name, version, modification time, ...</li>
+ * </ul>
+ *
+ * As the repository connects to some backend either locally or remote, the access to the repository may fail. The methods capsule the
+ * backend errors into <code>{@link MetadataRepositoryException}</code>.
+ *
+ * Facets are the way to provide additional metadata that is not part of the base API. It depends on the repository type (e.g. Maven, NPM,
+ * not the metadata backend) what facets are stored in addition to the standard metadata.
+ * Facets have a specific facet ID that represents the schema for the data stored. For creating specific objects for a given
+ * facet id the <code>{@link org.apache.archiva.metadata.model.MetadataFacetFactory}</code> is used.
+ * For each facet id there may exist multiple facet instances on each level. Facet instances are identified by their name, which may be
+ * a hierarchical path.
+ * The data in each facet instance is stored in properties (key-value pairs). The properties are converted into / from the specific
+ * facet object.
+ *
+ * Facets can be stored on repository, project, version and artifact level.
+ *
+ */
 public interface MetadataRepository
 {
     /**
-     * Update metadata for a particular project in the metadata repository, or create it if it does not already exist.
+     * Update metadata for a particular project in the metadata repository, or create it, if it does not already exist.
      *
-     * @param session
+     * @param session The session used for updating.
      * @param repositoryId the repository the project is in
      * @param project      the project metadata to create or update
+     * @throws MetadataRepositoryException if the update fails
      */
     void updateProject( RepositorySession session, String repositoryId, ProjectMetadata project )
         throws MetadataRepositoryException;
 
+    /**
+     * Update the metadata of a given artifact. If the artifact, namespace, version, project does not exist in the repository it will be created.
+     *
+     * @param session The repository session
+     * @param repositoryId The repository id
+     * @param namespace The namespace ('.' separated)
+     * @param projectId The project id
+     * @param projectVersion The project version
+     * @param artifactMeta Information about the artifact itself.
+     * @throws MetadataRepositoryException if something goes wrong during update.
+     */
     void updateArtifact( RepositorySession session, String repositoryId, String namespace, String projectId, String projectVersion,
                          ArtifactMetadata artifactMeta )
         throws MetadataRepositoryException;
 
+    /**
+     * Updates the metadata for a specific version of a given project. If the namespace, project, version does not exist,
+     * it will be created.
+     *
+     * @param session The repository session
+     * @param repositoryId The repository id
+     * @param namespace The namespace ('.' separated)
+     * @param projectId The project id
+     * @param versionMetadata The metadata for the version
+     * @throws MetadataRepositoryException if something goes wrong during update
+     */
     void updateProjectVersion( RepositorySession session, String repositoryId, String namespace, String projectId,
                                ProjectVersionMetadata versionMetadata )
         throws MetadataRepositoryException;
 
     /**
-     * create the namespace in the repository. (if not exist)
+     * Create the namespace in the repository, if it does not exist.
+     * Namespaces do not have specific metadata attached.
      *
-     *
-     * @param session
-     * @param repositoryId
-     * @param namespace
-     * @throws MetadataRepositoryException
+     * @param session The repository session
+     * @param repositoryId The repository id
+     * @param namespace The namespace ('.' separated)
+     * @throws MetadataRepositoryException if something goes wrong during update
      */
     void updateNamespace( RepositorySession session, String repositoryId, String namespace )
         throws MetadataRepositoryException;
 
+    /**
+     * Return the facet names stored for the given facet id on the repository level.
+     *
+     * @param session The repository session
+     * @param repositoryId The repository id
+     * @param facetId The facet id
+     * @return The list of facet names, or an empty list, if there are no facets stored on this repository for the given facet id.
+     * @throws MetadataRepositoryException if something goes wrong
+     */
     List<String> getMetadataFacets( RepositorySession session, String repositoryId, String facetId )
         throws MetadataRepositoryException;
 
+    <T extends MetadataFacet> Stream<T> getMetadataFacetStream( RepositorySession session, String repositoryId, Class<T> facetClazz)
+        throws MetadataRepositoryException;
+
+    <T extends MetadataFacet> Stream<T> getMetadataFacetStream( RepositorySession session, String repositoryId, Class<T> facetClazz, long offset, long maxEntries)
+        throws MetadataRepositoryException;
+
     /**
+     * Returns true, if there is facet data stored for the given id on the repository. The facet data itself
+     * may be empty. It's just checking if there is data stored for the given facet id.
      *
-     * @param session
-     * @param repositoryId
-     * @param facetId
-     * @return true if the repository datas for this facetId
-     * @throws MetadataRepositoryException
+     * @param session The repository session
+     * @param repositoryId The repository id
+     * @param facetId The facet id
+     * @return true if there is data stored this facetId on repository level.
+     * @throws MetadataRepositoryException if something goes wrong
      * @since 1.4-M4
      */
     boolean hasMetadataFacet( RepositorySession session, String repositoryId, String facetId )
         throws MetadataRepositoryException;
 
+    /**
+     * Returns the facet data stored on the repository level. The facet instance is identified by the facet id and the
+     * facet name. The returned object is a instance created by using <code>{@link org.apache.archiva.metadata.model.MetadataFacetFactory}</code>.
+     *
+     * @param session The repository session
+     * @param repositoryId The repository id
+     * @param facetId The facet id
+     * @param name The attribute name
+     * @return The facet values
+     * @throws MetadataRepositoryException if something goes wrong.
+     */
     MetadataFacet getMetadataFacet( RepositorySession session, String repositoryId, String facetId, String name )
         throws MetadataRepositoryException;
 
+    /**
+     * Returns the facet instance using the proper class.
+     *
+     * @param session The repository session
+     * @param repositoryId The repository
+     * @param clazz The facet object class
+     * @param name The name of the facet
+     * @param <T> The facet object
+     * @return The facet instance if it exists.
+     * @throws MetadataRepositoryException
+     */
+    <T extends MetadataFacet> T getMetadataFacet(RepositorySession session, String repositoryId, Class<T> clazz, String name)
+    throws MetadataRepositoryException;
+
     void addMetadataFacet( RepositorySession session, String repositoryId, MetadataFacet metadataFacet )
         throws MetadataRepositoryException;
 
@@ -102,6 +228,13 @@ public interface MetadataRepository
     List<ArtifactMetadata> getArtifactsByDateRange( RepositorySession session, String repositoryId, Date startTime, Date endTime )
         throws MetadataRepositoryException;
 
+    Stream<ArtifactMetadata> getArtifactsByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime )
+        throws MetadataRepositoryException;
+
+    Stream<ArtifactMetadata> getArtifactsByDateRangeStream( RepositorySession session, String repositoryId,
+                                                            ZonedDateTime startTime, ZonedDateTime endTime, long offset, long maxEntries )
+        throws MetadataRepositoryException;
+
     Collection<ArtifactMetadata> getArtifactsByChecksum( RepositorySession session, String repositoryId, String checksum )
         throws MetadataRepositoryException;
 
diff --git a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataResolver.java b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataResolver.java
index 4cd2cfd..aae2cf0 100644
--- a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataResolver.java
+++ b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataResolver.java
@@ -22,11 +22,19 @@ package org.apache.archiva.metadata.repository;
 import org.apache.archiva.metadata.model.ArtifactMetadata;
 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
 import org.apache.archiva.metadata.model.ProjectVersionReference;
+import org.apache.archiva.repository.Repository;
+import org.apache.archiva.repository.RepositoryType;
 
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.List;
 
 public interface MetadataResolver
 {
+    default List<RepositoryType> supportsRepositoryTypes() {
+        return Arrays.asList( RepositoryType.MAVEN );
+    }
+
     ProjectVersionMetadata resolveProjectVersion( RepositorySession session, String repoId, String namespace,
                                                   String projectId, String projectVersion )
         throws MetadataResolutionException;
diff --git a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataService.java b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataService.java
new file mode 100644
index 0000000..21c4f04
--- /dev/null
+++ b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataService.java
@@ -0,0 +1,100 @@
+package org.apache.archiva.metadata.repository;
+
+/*
+ * 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.MetadataFacet;
+import org.apache.archiva.metadata.model.MetadataFacetFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Service;
+
+import javax.inject.Inject;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author Martin Stockhammer <ma...@apache.org>
+ */
+
+@SuppressWarnings( "SpringJavaInjectionPointsAutowiringInspection" )
+@Service("metadataService")
+public class MetadataService
+{
+
+    private Map<String, MetadataFacetFactory<? extends MetadataFacet>> facetFactories = new HashMap<>( );
+    private Map<Class<? extends MetadataFacet>, MetadataFacetFactory<? extends MetadataFacet>> facetFactoriesByClass = new HashMap<>( );
+    private Map<String, Class<? extends MetadataFacet>> reverseFactoryMap = new HashMap<>( );
+
+    private MetadataResolver metadataResolver = null;
+
+    @Inject
+    ApplicationContext applicationContext;
+
+
+    @Inject
+    public void setMetadataFacetFactories( List<MetadataFacetFactory> factoryList ) {
+        Map<String, MetadataFacetFactory<? extends MetadataFacet>> facetFactories = new HashMap<>( );
+        Map<Class<? extends MetadataFacet>, MetadataFacetFactory<? extends MetadataFacet>> facetFactoriesByClass = new HashMap<>( );
+        Map<String, Class<? extends MetadataFacet>> reverseFactoryMap = new HashMap<>( );
+        for (MetadataFacetFactory factory : factoryList) {
+            facetFactories.put( factory.getFacetId( ), factory );
+            facetFactoriesByClass.put( factory.getFacetClass( ), factory );
+            reverseFactoryMap.put( factory.getFacetId( ), factory.getFacetClass( ) );
+        }
+        this.facetFactories = facetFactories;
+        this.facetFactoriesByClass = facetFactoriesByClass;
+        this.reverseFactoryMap = reverseFactoryMap;
+    }
+
+    public <T extends MetadataFacet> MetadataFacetFactory<T> getFactory(Class<T> facetClazz) {
+        return (MetadataFacetFactory<T>) facetFactoriesByClass.get( facetClazz );
+    }
+
+    public MetadataFacetFactory<?> getFactory(String facetId) {
+        return facetFactories.get( facetId );
+    }
+
+    public Set<String> getSupportedFacets() {
+        return facetFactories.keySet( );
+    }
+
+    public boolean supportsFacet(Class<? extends MetadataFacet> facetClazz) {
+        return facetFactoriesByClass.containsKey( facetClazz );
+    }
+
+    public boolean supportsFacet(String facetId) {
+        return facetFactories.containsKey( facetId );
+    }
+
+    public Class<? extends MetadataFacet> getFactoryClassForId( String facetId ) {
+        return reverseFactoryMap.get( facetId );
+    }
+
+    // Lazy evaluation to avoid problems with circular dependencies during initialization
+    public MetadataResolver getMetadataResolver()
+    {
+        if ( this.metadataResolver == null && applicationContext!=null)
+        {
+            this.metadataResolver = applicationContext.getBean( MetadataResolver.class );
+        }
+        return this.metadataResolver;
+    }
+}
diff --git a/archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/AbstractMetadataRepositoryTest.java b/archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/AbstractMetadataRepositoryTest.java
index a5574d6..ea47a00 100644
--- a/archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/AbstractMetadataRepositoryTest.java
+++ b/archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/AbstractMetadataRepositoryTest.java
@@ -23,6 +23,7 @@ import junit.framework.TestCase;
 import org.apache.archiva.metadata.generic.GenericMetadataFacet;
 import org.apache.archiva.metadata.generic.GenericMetadataFacetFactory;
 import org.apache.archiva.metadata.model.*;
+import org.apache.archiva.metadata.model.facets.AbstractMetadataFacetFactory;
 import org.apache.archiva.repository.Repository;
 import org.apache.archiva.test.utils.ArchivaSpringJUnit4ClassRunner;
 import org.junit.Before;
@@ -35,6 +36,8 @@ import org.springframework.test.context.ContextConfiguration;
 
 import java.text.SimpleDateFormat;
 import java.util.*;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import static org.assertj.core.api.Assertions.assertThat;
 
@@ -139,46 +142,43 @@ public abstract class AbstractMetadataRepositoryTest
     }
 
 
-    public static Map<String, MetadataFacetFactory> createTestMetadataFacetFactories( )
+    public static List<MetadataFacetFactory> createTestMetadataFacetFactories( )
     {
-        Map<String, MetadataFacetFactory> factories = new HashMap<>( );
-        factories.put( TEST_FACET_ID, new MetadataFacetFactory( )
+        List<MetadataFacetFactory> factories = new ArrayList<>( );
+        factories.add( new MetadataFacetFactory<TestMetadataFacet>( )
         {
             @Override
-            public MetadataFacet createMetadataFacet( )
+            public TestMetadataFacet createMetadataFacet( )
             {
                 return new TestMetadataFacet( TEST_METADATA_VALUE );
             }
 
             @Override
-            public MetadataFacet createMetadataFacet( String repositoryId, String name )
+            public TestMetadataFacet createMetadataFacet( String repositoryId, String name )
             {
                 return new TestMetadataFacet( TEST_METADATA_VALUE );
             }
-        } );
 
-        // add to ensure we don't accidentally create an empty facet ID.
-        factories.put( "", new MetadataFacetFactory( )
-        {
             @Override
-            public MetadataFacet createMetadataFacet( )
+            public Class<TestMetadataFacet> getFacetClass( )
             {
-                return new TestMetadataFacet( "", TEST_VALUE );
+                return TestMetadataFacet.class;
             }
 
             @Override
-            public MetadataFacet createMetadataFacet( String repositoryId, String name )
+            public String getFacetId( )
             {
-                return new TestMetadataFacet( "", TEST_VALUE );
+                return TEST_FACET_ID;
             }
         } );
 
         // for the getArtifactsByProjectVersionMetadata tests
-        factories.put( GenericMetadataFacet.FACET_ID, new GenericMetadataFacetFactory( ) );
+        factories.add( new GenericMetadataFacetFactory( ) );
 
         return factories;
     }
 
+
     @Test
     public void testRootNamespaceWithNoMetadataRepository( )
         throws Exception
@@ -794,6 +794,22 @@ public abstract class AbstractMetadataRepositoryTest
     }
 
     @Test
+    public void testGetMetadataFacetByClass( )
+        throws Exception
+    {
+        try ( RepositorySession session = getSessionFactory( ).createSession( ) )
+        {
+            getRepository( ).addMetadataFacet( session, TEST_REPO_ID, new TestMetadataFacet( TEST_VALUE ) );
+
+            TestMetadataFacet test =
+                (TestMetadataFacet) getRepository( ).getMetadataFacet( session, TEST_REPO_ID, TestMetadataFacet.class, TEST_NAME );
+
+            assertEquals( new TestMetadataFacet( TEST_VALUE ), test );
+
+        }
+    }
+
+    @Test
     public void testGetMetadataFacetWhenEmpty( )
         throws Exception
     {
@@ -858,6 +874,28 @@ public abstract class AbstractMetadataRepositoryTest
     }
 
     @Test
+    public void testGetMetadataFacetsStream( )
+        throws Exception
+    {
+        try ( RepositorySession session = getSessionFactory( ).createSession( ) )
+        {
+            getRepository( ).addMetadataFacet( session, TEST_REPO_ID, new TestMetadataFacet( TEST_VALUE ) );
+        }
+
+        try ( RepositorySession session = getSessionFactory( ).createSession( ) )
+        {
+            tryAssert( ( ) -> {
+                Stream<TestMetadataFacet> str = getRepository( ).getMetadataFacetStream( session, TEST_REPO_ID, TestMetadataFacet.class );
+                assertNotNull( str );
+                List<TestMetadataFacet> result = str.collect( Collectors.toList( ) );
+                assertEquals( 1, result.size( ) );
+                assertEquals( TEST_NAME, result.get( 0 ).getName( ) );
+            } );
+
+        }
+    }
+
+    @Test
     public void testGetMetadataFacetsWhenEmpty( )
         throws Exception
     {
@@ -1617,7 +1655,7 @@ public abstract class AbstractMetadataRepositoryTest
                 assertThat( artifactMetadata.getRepositoryId( ) ).isEqualTo( TEST_REPO_ID );
                 MetadataFacet facet = artifactMetadata.getFacet( TEST_FACET_ID );
                 assertThat( facet ).isNotNull( );
-                assertThat( facet.toProperties( ) ).isEqualTo( Collections.singletonMap( "foo", TEST_METADATA_VALUE ) );
+                assertThat( facet.toProperties( ).get("foo").equals(TEST_METADATA_VALUE) );
             } );
         }
     }
diff --git a/archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/AuditEventFactory.java b/archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/AuditEventFactory.java
index 620100f..c76be7e 100644
--- a/archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/AuditEventFactory.java
+++ b/archiva-modules/plugins/audit/src/main/java/org/apache/archiva/audit/AuditEventFactory.java
@@ -19,8 +19,8 @@ package org.apache.archiva.audit;
  * under the License.
  */
 
-import org.apache.archiva.metadata.model.MetadataFacet;
 import org.apache.archiva.metadata.model.MetadataFacetFactory;
+import org.apache.archiva.metadata.model.facets.AbstractMetadataFacetFactory;
 import org.apache.archiva.metadata.model.facets.AuditEvent;
 import org.springframework.stereotype.Service;
 
@@ -29,17 +29,33 @@ import org.springframework.stereotype.Service;
  */
 @Service("metadataFacetFactory#org.apache.archiva.audit")
 public class AuditEventFactory
-    implements MetadataFacetFactory
+    extends AbstractMetadataFacetFactory<AuditEvent>
 {
+    public AuditEventFactory() {
+        super( AuditEvent.class );
+    }
+
     @Override
-    public MetadataFacet createMetadataFacet()
+    public AuditEvent createMetadataFacet()
     {
         throw new UnsupportedOperationException( "Must construct an audit event with a name" );
     }
 
     @Override
-    public MetadataFacet createMetadataFacet( String repositoryId, String name )
+    public AuditEvent createMetadataFacet( String repositoryId, String name )
     {
         return new AuditEvent( name, repositoryId );
     }
+
+    @Override
+    public boolean assignsFacet( Class<?> clazz )
+    {
+        return false;
+    }
+
+    @Override
+    public Class<AuditEvent> getFacetClass( )
+    {
+        return null;
+    }
 }
diff --git a/archiva-modules/plugins/generic-metadata-support/src/main/java/org/apache/archiva/metadata/generic/GenericMetadataFacetFactory.java b/archiva-modules/plugins/generic-metadata-support/src/main/java/org/apache/archiva/metadata/generic/GenericMetadataFacetFactory.java
index 8d4dfb6..b5aad19 100644
--- a/archiva-modules/plugins/generic-metadata-support/src/main/java/org/apache/archiva/metadata/generic/GenericMetadataFacetFactory.java
+++ b/archiva-modules/plugins/generic-metadata-support/src/main/java/org/apache/archiva/metadata/generic/GenericMetadataFacetFactory.java
@@ -19,25 +19,28 @@ package org.apache.archiva.metadata.generic;
  * under the License.
  */
 
-import org.apache.archiva.metadata.model.MetadataFacet;
-import org.apache.archiva.metadata.model.MetadataFacetFactory;
+import org.apache.archiva.metadata.model.facets.AbstractMetadataFacetFactory;
 import org.springframework.stereotype.Service;
 
 /**
  */
 @Service("metadataFacetFactory#org.apache.archiva.metadata.generic")
 public class GenericMetadataFacetFactory
-    implements MetadataFacetFactory
+    extends AbstractMetadataFacetFactory<GenericMetadataFacet>
 {
 
+    public GenericMetadataFacetFactory() {
+        super( GenericMetadataFacet.class );
+    }
+
     @Override
-    public MetadataFacet createMetadataFacet()
+    public GenericMetadataFacet createMetadataFacet()
     {
         return new GenericMetadataFacet();
     }
 
     @Override
-    public MetadataFacet createMetadataFacet( String repositoryId, String name )
+    public GenericMetadataFacet createMetadataFacet( String repositoryId, String name )
     {
         throw new UnsupportedOperationException( "There is no valid name for project version facets" );
     }
diff --git a/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepository.java b/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepository.java
index 00cef08..4a2c8b0 100644
--- a/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepository.java
+++ b/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepository.java
@@ -38,6 +38,7 @@ import org.apache.archiva.metadata.model.maven2.MavenArtifactFacet;
 import org.apache.archiva.metadata.repository.MetadataRepository;
 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
 import org.apache.archiva.metadata.repository.MetadataResolutionException;
+import org.apache.archiva.metadata.repository.MetadataService;
 import org.apache.archiva.metadata.repository.RepositorySession;
 import org.apache.archiva.metadata.repository.stats.model.RepositoryStatistics;
 import org.apache.archiva.metadata.repository.stats.model.RepositoryStatisticsProvider;
@@ -46,6 +47,7 @@ import org.apache.jackrabbit.JcrConstants;
 import org.apache.jackrabbit.commons.JcrUtils;
 import org.apache.jackrabbit.commons.cnd.CndImporter;
 import org.apache.jackrabbit.commons.cnd.ParseException;
+import org.jetbrains.annotations.Nullable;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -57,10 +59,9 @@ import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.SimpleCredentials;
+import javax.jcr.Value;
 import javax.jcr.ValueFactory;
 import javax.jcr.Workspace;
-import javax.jcr.nodetype.NodeTypeManager;
-import javax.jcr.nodetype.NodeTypeTemplate;
 import javax.jcr.query.Query;
 import javax.jcr.query.QueryManager;
 import javax.jcr.query.QueryResult;
@@ -69,6 +70,7 @@ import javax.jcr.query.RowIterator;
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
+import java.time.ZonedDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Calendar;
@@ -82,10 +84,14 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.Spliterator;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
 
 import static javax.jcr.Property.JCR_LAST_MODIFIED;
-import static org.apache.archiva.metadata.repository.jcr.JcrConstants.DEPENDENCY_NODE_TYPE;
-import static org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_VERSION_VERSION_PROPERTIES;
+import static org.apache.archiva.metadata.repository.jcr.JcrConstants.*;
 
 /**
  * TODO below: revise storage format for project version metadata
@@ -96,34 +102,34 @@ public class JcrMetadataRepository
 {
 
 
-    private static final String QUERY_ARTIFACT_1 = "SELECT * FROM [" + org.apache.archiva.metadata.repository.jcr.JcrConstants.ARTIFACT_NODE_TYPE + "] AS artifact WHERE ISDESCENDANTNODE(artifact,'/";
+    private static final String QUERY_ARTIFACT_1 = "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact WHERE ISDESCENDANTNODE(artifact,'/";
 
-    static final String QUERY_ARTIFACTS_BY_PROJECT_VERSION_1 = "SELECT * FROM [" + org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_VERSION_NODE_TYPE + "] AS projectVersion INNER JOIN [" + org.apache.archiva.metadata.repository.jcr.JcrConstants.ARTIFACT_NODE_TYPE
-        + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) INNER JOIN [" + org.apache.archiva.metadata.repository.jcr.JcrConstants.FACET_NODE_TYPE
+    static final String QUERY_ARTIFACTS_BY_PROJECT_VERSION_1 = "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE + "] AS projectVersion INNER JOIN [" + ARTIFACT_NODE_TYPE
+        + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) INNER JOIN [" + FACET_NODE_TYPE
         + "] AS facet ON ISCHILDNODE(facet, projectVersion) WHERE ([facet].[";
     static final String QUERY_ARTIFACTS_BY_PROJECT_VERSION_2= "] = $value)";
 
-    static final String QUERY_ARTIFACTS_BY_METADATA_1 = "SELECT * FROM [" + org.apache.archiva.metadata.repository.jcr.JcrConstants.ARTIFACT_NODE_TYPE + "] AS artifact INNER JOIN [" + org.apache.archiva.metadata.repository.jcr.JcrConstants.FACET_NODE_TYPE
+    static final String QUERY_ARTIFACTS_BY_METADATA_1 = "SELECT * FROM [" + ARTIFACT_NODE_TYPE + "] AS artifact INNER JOIN [" + FACET_NODE_TYPE
         + "] AS facet ON ISCHILDNODE(facet, artifact) WHERE ([facet].[";
     static final String QUERY_ARTIFACTS_BY_METADATA_2 = "] = $value)";
 
-    static final String QUERY_ARTIFACTS_BY_PROPERTY_1 = "SELECT * FROM [" + org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_VERSION_NODE_TYPE + "] AS projectVersion INNER JOIN [" + org.apache.archiva.metadata.repository.jcr.JcrConstants.ARTIFACT_NODE_TYPE
+    static final String QUERY_ARTIFACTS_BY_PROPERTY_1 = "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE + "] AS projectVersion INNER JOIN [" + ARTIFACT_NODE_TYPE
            + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) WHERE ([projectVersion].[";
     static final String QUERY_ARTIFACTS_BY_PROPERTY_2 = "] = $value)";
 
 
     private static final String QUERY_ARTIFACT_2 = "')";
 
-    private final Map<String, MetadataFacetFactory> metadataFacetFactories;
+    private MetadataService metadataService;
 
     private Logger log = LoggerFactory.getLogger( JcrMetadataRepository.class );
 
     private Repository repository;
 
-    public JcrMetadataRepository( Map<String, MetadataFacetFactory> metadataFacetFactories, Repository repository )
+    public JcrMetadataRepository( MetadataService metadataService, Repository repository )
         throws RepositoryException
     {
-        this.metadataFacetFactories = metadataFacetFactories;
+        this.metadataService = metadataService;
         this.repository = repository;
     }
 
@@ -146,7 +152,6 @@ public class JcrMetadataRepository
             registry.registerNamespace( "archiva", "http://archiva.apache.org/jcr/" );
         }
 
-        NodeTypeManager nodeTypeManager = workspace.getNodeTypeManager();
         try(
             Reader cndReader = new InputStreamReader(
                 Thread.currentThread( ).getContextClassLoader( ).getResourceAsStream( "org/apache/archiva/metadata/repository/jcr/jcr-schema.cnd" ) ))
@@ -162,31 +167,6 @@ public class JcrMetadataRepository
             e.printStackTrace( );
         }
 
-
-//        registerMixinNodeType( nodeTypeManager, REPOSITORY_NODE_TYPE );
-//        registerMixinNodeType( nodeTypeManager, NAMESPACE_NODE_TYPE );
-//        registerMixinNodeType( nodeTypeManager, PROJECT_NODE_TYPE );
-//        registerMixinNodeType( nodeTypeManager, PROJECT_VERSION_NODE_TYPE );
-//        registerMixinNodeType( nodeTypeManager, ARTIFACT_NODE_TYPE );
-//        registerMixinNodeType( nodeTypeManager, FACET_NODE_TYPE );
-//        registerMixinNodeType( nodeTypeManager, DEPENDENCY_NODE_TYPE );
-
-
-    }
-
-    private static void registerMixinNodeType( NodeTypeManager nodeTypeManager, String name )
-        throws RepositoryException
-    {
-        // for now just don't re-create - but in future if we change the definition, make sure to remove first as an
-        // upgrade path
-        if ( !nodeTypeManager.hasNodeType( name ) )
-        {
-            NodeTypeTemplate nodeType = nodeTypeManager.createNodeTypeTemplate();
-            nodeType.setMixin( true );
-            nodeType.setName( name );
-            nodeType.setQueryable( true );
-            nodeTypeManager.registerNodeType( nodeType, false );
-        }
     }
 
     private Session getSession(RepositorySession repositorySession) throws MetadataRepositoryException {
@@ -249,7 +229,7 @@ public class JcrMetadataRepository
             node.setProperty( "version", artifactMeta.getVersion() );
 
             // iterate over available facets to update/add/remove from the artifactMetadata
-            for ( String facetId : metadataFacetFactories.keySet() )
+            for ( String facetId : metadataService.getSupportedFacets() )
             {
                 MetadataFacet metadataFacet = artifactMeta.getFacet( facetId );
                 if ( metadataFacet == null )
@@ -264,7 +244,8 @@ public class JcrMetadataRepository
                 {
                     // recreate, to ensure properties are removed
                     Node n = node.addNode( facetId);
-                    n.addMixin( org.apache.archiva.metadata.repository.jcr.JcrConstants.FACET_NODE_TYPE );
+                    n.addMixin( FACET_NODE_TYPE );
+                    n.setProperty( "facetId", facetId );
 
                     for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
                     {
@@ -397,7 +378,7 @@ public class JcrMetadataRepository
                     versionNode.getNode( facet.getFacetId() ).remove();
                 }
                 Node n = versionNode.addNode( facet.getFacetId() );
-                n.addMixin( org.apache.archiva.metadata.repository.jcr.JcrConstants.FACET_NODE_TYPE );
+                n.addMixin( FACET_NODE_TYPE );
 
                 for ( Map.Entry<String, String> entry : facet.toProperties().entrySet() )
                 {
@@ -513,6 +494,84 @@ public class JcrMetadataRepository
         return facets;
     }
 
+    private <T> Spliterator<T> createResultSpliterator(QueryResult result, Function<Row, T> converter) throws MetadataRepositoryException
+    {
+        final RowIterator rowIterator;
+        try
+        {
+            rowIterator = result.getRows();
+        }
+        catch ( RepositoryException e )
+        {
+            throw new MetadataRepositoryException( e.getMessage( ), e );
+        }
+        return new Spliterator<T>( )
+        {
+            @Override
+            public boolean tryAdvance( Consumer<? super T> action )
+            {
+                while (rowIterator.hasNext()) {
+                    T item = converter.apply( rowIterator.nextRow() );
+                    if (item!=null)
+                    {
+                        action.accept( item );
+                        return true;
+                    }
+                }
+                return false;
+            }
+
+            @Override
+            public Spliterator<T> trySplit( )
+            {
+                return null;
+            }
+
+            @Override
+            public long estimateSize( )
+            {
+                return 0;
+            }
+
+            @Override
+            public int characteristics( )
+            {
+                return ORDERED+NONNULL;
+            }
+        };
+    }
+
+    @Override
+    public <T extends MetadataFacet> Stream<T> getMetadataFacetStream( RepositorySession session, String repositoryId, Class<T> facetClazz ) throws MetadataRepositoryException
+    {
+        final Session jcrSession = getSession( session );
+        final MetadataFacetFactory<T> factory = metadataService.getFactory( facetClazz );
+        final String facetId = factory.getFacetId( );
+        final String facetPath = getFacetPath( repositoryId, facetId );
+        String q = "SELECT * FROM ["+FACET_NODE_TYPE+"] AS facet WHERE ISDESCENDANTNODE(facet, [/"+facetPath+"])";
+        Map<String, String> params = new HashMap<>( );
+        QueryResult result = runNativeJcrQuery( jcrSession, q, params );
+        return StreamSupport.stream( createResultSpliterator( result, (Row row)-> {
+            try
+            {
+                Node node = row.getNode( "facet" );
+                String path = StringUtils.removeStart( node.getPath(), facetPath);
+                return createFacet( factory, node, repositoryId, path );
+            }
+            catch ( RepositoryException e )
+            {
+                return null;
+            }
+        }), false );
+
+    }
+
+    @Override
+    public <T extends MetadataFacet> Stream<T> getMetadataFacetStream( RepositorySession session, String repositoryId, Class<T> facetClazz, long offset, long maxEntries ) throws MetadataRepositoryException
+    {
+        return null;
+    }
+
     private void recurse( List<String> facets, String prefix, Node node )
         throws RepositoryException
     {
@@ -531,37 +590,28 @@ public class JcrMetadataRepository
         }
     }
 
+
     @Override
-    public MetadataFacet getMetadataFacet( RepositorySession session, String repositoryId, String facetId, String name )
-        throws MetadataRepositoryException
+    public <T extends MetadataFacet> T getMetadataFacet( RepositorySession session, String repositoryId, Class<T> clazz, String name ) throws MetadataRepositoryException
     {
+        if (!metadataService.supportsFacet( clazz )) {
+            log.warn( "The required metadata class is not supported: " + clazz );
+            return null;
+        }
         final Session jcrSession = getSession( session );
-        MetadataFacet metadataFacet = null;
+        final MetadataFacetFactory<T> factory = metadataService.getFactory( clazz );
+        final String facetId = factory.getFacetId( );
         try
         {
             Node root = jcrSession.getRootNode();
             Node node = root.getNode( getFacetPath( repositoryId, facetId, name ) );
 
-            if ( metadataFacetFactories == null )
+            if ( metadataService.getSupportedFacets().size()==0)
             {
-                return metadataFacet;
+                return null;
             }
 
-            MetadataFacetFactory metadataFacetFactory = metadataFacetFactories.get( facetId );
-            if ( metadataFacetFactory != null )
-            {
-                metadataFacet = metadataFacetFactory.createMetadataFacet( repositoryId, name );
-                Map<String, String> map = new HashMap<>();
-                for ( Property property : JcrUtils.getProperties( node ) )
-                {
-                    String p = property.getName();
-                    if ( !p.startsWith( "jcr:" ) )
-                    {
-                        map.put( p, property.getString() );
-                    }
-                }
-                metadataFacet.fromProperties( map );
-            }
+            return createFacet( factory, node, repositoryId, name );
         }
         catch ( PathNotFoundException e )
         {
@@ -571,7 +621,35 @@ public class JcrMetadataRepository
         {
             throw new MetadataRepositoryException( e.getMessage(), e );
         }
-        return metadataFacet;
+        return null;
+    }
+
+    @Nullable
+    private <T extends MetadataFacet> T createFacet( MetadataFacetFactory<T> factory, Node node, String repositoryId, String name ) throws RepositoryException
+    {
+        if ( factory != null )
+        {
+            T metadataFacet = factory.createMetadataFacet( repositoryId, name );
+            Map<String, String> map = new HashMap<>();
+            for ( Property property : JcrUtils.getProperties( node ) )
+            {
+                String p = property.getName();
+                if ( !p.startsWith( "jcr:" ) )
+                {
+                    map.put( p, property.getString() );
+                }
+            }
+            metadataFacet.fromProperties( map );
+            return metadataFacet;
+        }
+        return null;
+    }
+
+    @Override
+    public MetadataFacet getMetadataFacet( RepositorySession session, String repositoryId, String facetId, String name )
+        throws MetadataRepositoryException
+    {
+        return getMetadataFacet( session, repositoryId, metadataService.getFactoryClassForId( facetId ), name );
     }
 
     @Override
@@ -588,6 +666,11 @@ public class JcrMetadataRepository
             Node facetNode = JcrUtils.getOrAddNode( facets, id );
 
             Node node = getOrAddNodeByPath( facetNode, metadataFacet.getName() );
+            if (!node.isNodeType( FACET_NODE_TYPE ))
+            {
+                node.addMixin( FACET_NODE_TYPE );
+                node.setProperty( "facetId", id );
+            }
 
             for ( Map.Entry<String, String> entry : metadataFacet.toProperties().entrySet() )
             {
@@ -718,6 +801,18 @@ public class JcrMetadataRepository
         return artifacts;
     }
 
+    @Override
+    public Stream<ArtifactMetadata> getArtifactsByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime ) throws MetadataRepositoryException
+    {
+        return null;
+    }
+
+    @Override
+    public Stream<ArtifactMetadata> getArtifactsByDateRangeStream( RepositorySession session, String repositoryId, ZonedDateTime startTime, ZonedDateTime endTime, long offset, long maxEntries ) throws MetadataRepositoryException
+    {
+        return null;
+    }
+
 
     @Override
     public List<ArtifactMetadata> getArtifactsByChecksum( RepositorySession session, String repositoryId, String checksum )
@@ -804,13 +899,18 @@ public class JcrMetadataRepository
 
         try
         {
+            log.debug( "Query: {}", q );
             Query query = jcrSession.getWorkspace().getQueryManager().createQuery( q, Query.JCR_SQL2 );
             ValueFactory valueFactory = jcrSession.getValueFactory();
             for ( Entry<String, String> entry : bindings.entrySet() )
             {
-                query.bindValue( entry.getKey(), valueFactory.createValue( entry.getValue() ) );
+                log.debug( "Binding: {}={}", entry.getKey( ), entry.getValue( ) );
+                Value value = valueFactory.createValue( entry.getValue( ) );
+                log.debug( "Binding value {}={}", entry.getKey( ), value);
+                query.bindValue( entry.getKey(), value );
             }
             long start = System.currentTimeMillis( );
+            log.debug( "Execute query {}", query );
             QueryResult result = query.execute();
             long end = System.currentTimeMillis( );
             log.info( "JCR Query ran in {} milliseconds: {}", end - start, q );
@@ -889,7 +989,7 @@ public class JcrMetadataRepository
             artifacts = new ArrayList<>();
             for ( Node n : JcrUtils.getNodes( result ) )
             {
-                if ( n.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.ARTIFACT_NODE_TYPE ) )
+                if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
                 {
                     artifacts.add( getArtifactFromNode( repositoryId, n ) );
                 }
@@ -1109,10 +1209,10 @@ public class JcrMetadataRepository
     {
         for ( Node n : JcrUtils.getChildNodes( node ) )
         {
-            if ( n.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.FACET_NODE_TYPE ) )
+            if ( n.isNodeType( FACET_NODE_TYPE ) )
             {
                 String name = n.getName();
-                MetadataFacetFactory factory = metadataFacetFactories.get( name );
+                MetadataFacetFactory factory = metadataService.getFactory( name );
                 if ( factory == null )
                 {
                     log.error( "Attempted to load unknown project version metadata facet: {}", name );
@@ -1278,7 +1378,7 @@ public class JcrMetadataRepository
     {
         try
         {
-            return getNodeNames( getSession(session), getProjectPath( repositoryId, namespace, projectId ), org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_VERSION_NODE_TYPE );
+            return getNodeNames( getSession(session), getProjectPath( repositoryId, namespace, projectId ), PROJECT_VERSION_NODE_TYPE );
         }
         catch ( MetadataRepositoryException e )
         {
@@ -1306,7 +1406,7 @@ public class JcrMetadataRepository
 
                 for ( Node n : JcrUtils.getChildNodes( node ) )
                 {
-                    if ( n.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.ARTIFACT_NODE_TYPE ) )
+                    if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
                     {
                         if ( n.hasProperty( "version" ) )
                         {
@@ -1345,7 +1445,7 @@ public class JcrMetadataRepository
 
             for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
             {
-                if ( node.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_VERSION_NODE_TYPE ) && StringUtils.equals( projectVersion,
+                if ( node.isNodeType( PROJECT_VERSION_NODE_TYPE ) && StringUtils.equals( projectVersion,
                                                                                          node.getName() ) )
                 {
                     node.remove();
@@ -1381,7 +1481,7 @@ public class JcrMetadataRepository
 
             for ( Node node : JcrUtils.getChildNodes( nodeAtPath ) )
             {
-                if ( node.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_VERSION_NODE_TYPE ) //
+                if ( node.isNodeType( PROJECT_VERSION_NODE_TYPE ) //
                     && StringUtils.equals( node.getName(), projectVersion ) )
                 {
                     node.remove();
@@ -1411,7 +1511,7 @@ public class JcrMetadataRepository
 
                 for ( Node n : JcrUtils.getChildNodes( node ) )
                 {
-                    if ( n.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.ARTIFACT_NODE_TYPE ) )
+                    if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
                     {
                         ArtifactMetadata artifactMetadata = getArtifactFromNode( repositoryId, n );
                         log.debug( "artifactMetadata: {}", artifactMetadata );
@@ -1457,7 +1557,7 @@ public class JcrMetadataRepository
 
                 for ( Node n : JcrUtils.getChildNodes( node ) )
                 {
-                    if ( n.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.ARTIFACT_NODE_TYPE ) )
+                    if ( n.isNodeType( ARTIFACT_NODE_TYPE ) )
                     {
                         artifacts.add( getArtifactFromNode( repositoryId, n ) );
                     }
@@ -1526,15 +1626,15 @@ public class JcrMetadataRepository
         {
             // We search only for project version properties if the key is a valid property name
             String q1 =
-                "SELECT * FROM [" + org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_VERSION_NODE_TYPE
-                    + "] AS projectVersion LEFT OUTER JOIN [" + org.apache.archiva.metadata.repository.jcr.JcrConstants.ARTIFACT_NODE_TYPE
+                "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE
+                    + "] AS projectVersion LEFT OUTER JOIN [" + ARTIFACT_NODE_TYPE
                     + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) WHERE " + projectVersionCondition + descendantCondition;
              result.addAll(runJcrQuery( jcrSession, repositoryId, q1, ImmutableMap.of( "value", text ), false ));
         }
         String q2 =
-            "SELECT * FROM [" + org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_VERSION_NODE_TYPE
-                + "] AS projectVersion LEFT OUTER JOIN [" + org.apache.archiva.metadata.repository.jcr.JcrConstants.ARTIFACT_NODE_TYPE
-                + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) LEFT OUTER JOIN [" + org.apache.archiva.metadata.repository.jcr.JcrConstants.FACET_NODE_TYPE
+            "SELECT * FROM [" + PROJECT_VERSION_NODE_TYPE
+                + "] AS projectVersion LEFT OUTER JOIN [" + ARTIFACT_NODE_TYPE
+                + "] AS artifact ON ISCHILDNODE(artifact, projectVersion) LEFT OUTER JOIN [" + FACET_NODE_TYPE
                 + "] AS facet ON ISCHILDNODE(facet, projectVersion) WHERE " + facetCondition + descendantCondition;
         result.addAll( runJcrQuery( jcrSession, repositoryId, q2, ImmutableMap.of( "value", text ), false ) );
         return result;
@@ -1639,7 +1739,8 @@ public class JcrMetadataRepository
 
     private static String getFacetPath( String repositoryId, String facetId )
     {
-        return getRepositoryPath( repositoryId ) + "/facets/" + facetId;
+        return StringUtils.isEmpty( facetId ) ? getRepositoryPath( repositoryId ) + "/facets" :
+            getRepositoryPath( repositoryId ) + "/facets/" + facetId;
     }
 
     private static String getNamespacePath( String repositoryId, String namespace )
@@ -1748,9 +1849,9 @@ public class JcrMetadataRepository
         Node projectNode = getOrAddProjectNode( jcrSession, repositoryId, namespace, projectId );
         log.debug( "Project node {}", projectNode );
         Node projectVersionNode = JcrUtils.getOrAddNode( projectNode, projectVersion, JcrConstants.NT_UNSTRUCTURED);
-        if (!projectVersionNode.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_VERSION_NODE_TYPE ))
+        if (!projectVersionNode.isNodeType( PROJECT_VERSION_NODE_TYPE ))
         {
-            projectVersionNode.addMixin( org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_VERSION_NODE_TYPE );
+            projectVersionNode.addMixin( PROJECT_VERSION_NODE_TYPE );
         }
         if (!projectVersionNode.hasProperty( "id" ))
         {
@@ -1767,9 +1868,9 @@ public class JcrMetadataRepository
     {
         Node versionNode = getOrAddProjectVersionNode( jcrSession, repositoryId, namespace, projectId, projectVersion );
         Node node = JcrUtils.getOrAddNode( versionNode, id);
-        if (!node.isNodeType( org.apache.archiva.metadata.repository.jcr.JcrConstants.ARTIFACT_NODE_TYPE ))
+        if (!node.isNodeType( ARTIFACT_NODE_TYPE ))
         {
-            node.addMixin( org.apache.archiva.metadata.repository.jcr.JcrConstants.ARTIFACT_NODE_TYPE );
+            node.addMixin( ARTIFACT_NODE_TYPE );
         }
         if (!node.hasProperty( "id" )) {
             node.setProperty( "id", id );
diff --git a/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrRepositorySessionFactory.java b/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrRepositorySessionFactory.java
index f1f6798..0469b87 100644
--- a/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrRepositorySessionFactory.java
+++ b/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/JcrRepositorySessionFactory.java
@@ -19,6 +19,7 @@ package org.apache.archiva.metadata.repository.jcr;
  * under the License.
  */
 
+import org.apache.archiva.metadata.model.MetadataFacet;
 import org.apache.archiva.metadata.model.MetadataFacetFactory;
 import org.apache.archiva.metadata.repository.*;
 import org.apache.commons.lang.StringUtils;
@@ -49,11 +50,6 @@ public class JcrRepositorySessionFactory extends AbstractRepositorySessionFactor
 
     private Logger logger = LoggerFactory.getLogger( getClass() );
 
-    @Inject
-    private ApplicationContext applicationContext;
-
-    private Map<String, MetadataFacetFactory> metadataFacetFactories;
-
     private Repository repository;
 
     // Lazy evaluation to avoid problems with circular dependencies during initialization
@@ -62,6 +58,9 @@ public class JcrRepositorySessionFactory extends AbstractRepositorySessionFactor
     @Inject
     private RepositorySessionFactoryBean repositorySessionFactoryBean;
 
+    @Inject
+    private MetadataService metadataService;
+
     private OakRepositoryFactory repositoryFactory;
 
     private JcrMetadataRepository jcrMetadataRepository;
@@ -80,14 +79,8 @@ public class JcrRepositorySessionFactory extends AbstractRepositorySessionFactor
         }
     }
 
-    // Lazy evaluation to avoid problems with circular dependencies during initialization
-    private MetadataResolver getMetadataResolver()
-    {
-        if ( this.metadataResolver == null && applicationContext!=null)
-        {
-            this.metadataResolver = applicationContext.getBean( MetadataResolver.class );
-        }
-        return this.metadataResolver;
+    private MetadataResolver getMetadataResolver() {
+        return metadataService.getMetadataResolver( );
     }
 
     protected void initialize()
@@ -102,27 +95,6 @@ public class JcrRepositorySessionFactory extends AbstractRepositorySessionFactor
         StopWatch stopWatch = new StopWatch();
         stopWatch.start();
 
-        if (applicationContext!=null) {
-            metadataFacetFactories = applicationContext.getBeansOfType(MetadataFacetFactory.class);
-        }
-        // olamy with spring the "id" is now "metadataFacetFactory#hint"
-        // whereas was only hint with plexus so let remove  metadataFacetFactory#
-        Map<String, MetadataFacetFactory> cleanedMetadataFacetFactories =
-            new HashMap<>( metadataFacetFactories.size() );
-
-        for ( Map.Entry<String, MetadataFacetFactory> entry : metadataFacetFactories.entrySet() )
-        {
-            if (entry.getKey().contains("#")) {
-                cleanedMetadataFacetFactories.put( StringUtils.substringAfterLast( entry.getKey(), "#" ),
-                        entry.getValue() );
-
-            } else {
-                cleanedMetadataFacetFactories.put(entry.getKey(), entry.getValue());
-            }
-        }
-
-        metadataFacetFactories = cleanedMetadataFacetFactories;
-
         try
         {
 
@@ -136,7 +108,7 @@ public class JcrRepositorySessionFactory extends AbstractRepositorySessionFactor
                 logger.error("Repository creation failed {}", e.getMessage());
                 throw new RuntimeException("Fatal error. Could not create metadata repository.");
             }
-            jcrMetadataRepository = new JcrMetadataRepository( metadataFacetFactories, repository );
+            jcrMetadataRepository = new JcrMetadataRepository( metadataService, repository );
             try ( JcrRepositorySession session = new JcrRepositorySession( jcrMetadataRepository, metadataResolver )) {
                 JcrMetadataRepository.initializeNodeTypes( session.getJcrSession() );
                 // Saves automatically with close
@@ -171,8 +143,14 @@ public class JcrRepositorySessionFactory extends AbstractRepositorySessionFactor
         return jcrMetadataRepository;
     }
 
-    public void setMetadataFacetFactories(Map<String, MetadataFacetFactory> metadataFacetFactories) {
-        this.metadataFacetFactories = metadataFacetFactories;
+
+    public MetadataService getMetadataService( )
+    {
+        return metadataService;
     }
 
+    public void setMetadataService( MetadataService metadataService )
+    {
+        this.metadataService = metadataService;
+    }
 }
diff --git a/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/OakRepositoryFactory.java b/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/OakRepositoryFactory.java
index 677bf27..e9982dc 100644
--- a/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/OakRepositoryFactory.java
+++ b/archiva-modules/plugins/metadata-store-jcr/src/main/java/org/apache/archiva/metadata/repository/jcr/OakRepositoryFactory.java
@@ -419,7 +419,6 @@ public class OakRepositoryFactory
 
             private IndexDefinitionBuilder.PropertyRule initBaseRule( IndexDefinitionBuilder.IndexRule rule ) {
                 return rule
-                    .sync()
                     .indexNodeName( )
                     .property(JCR_CREATED).propertyIndex().type("Date").ordered()
                     .property(JCR_LASTMODIFIED ).propertyIndex().type( "Date" ).ordered()
@@ -486,7 +485,8 @@ public class OakRepositoryFactory
                         .property( "whenGathered" ).type("Date").propertyIndex().analyzed().ordered()
                         .property("size").type("Long").propertyIndex().analyzed().ordered()
                         .property("version").propertyIndex().analyzed().ordered();
-                    initRegexAll( idxBuilder.indexRule( FACET_NODE_TYPE ) );
+                    initRegexAll( idxBuilder.indexRule( FACET_NODE_TYPE ) )
+                        .property("facetId").propertyIndex().analyzed().ordered();
                     idxBuilder.indexRule( MIXIN_META_SCM )
                         .property( "scm.connection" ).propertyIndex()
                         .property( "scm.developerConnection" ).propertyIndex()
diff --git a/archiva-modules/plugins/metadata-store-jcr/src/main/resources/org/apache/archiva/metadata/repository/jcr/jcr-schema.cnd b/archiva-modules/plugins/metadata-store-jcr/src/main/resources/org/apache/archiva/metadata/repository/jcr/jcr-schema.cnd
index e63d5fd..9b56d3f 100644
--- a/archiva-modules/plugins/metadata-store-jcr/src/main/resources/org/apache/archiva/metadata/repository/jcr/jcr-schema.cnd
+++ b/archiva-modules/plugins/metadata-store-jcr/src/main/resources/org/apache/archiva/metadata/repository/jcr/jcr-schema.cnd
@@ -105,4 +105,5 @@
  + checksum (archiva:checksum) multiple
  + * (archiva:facet) multiple
 
-[archiva:facet] > archiva:base mixin
\ No newline at end of file
+[archiva:facet] > archiva:base mixin
+ - facetId
\ No newline at end of file
diff --git a/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepositoryTest.java b/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepositoryTest.java
index 171d6be..58bb441 100644
--- a/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepositoryTest.java
+++ b/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/jcr/JcrMetadataRepositoryTest.java
@@ -24,6 +24,7 @@ import org.apache.archiva.metadata.model.MetadataFacetFactory;
 import org.apache.archiva.metadata.repository.AbstractMetadataRepositoryTest;
 import org.apache.archiva.metadata.repository.DefaultMetadataResolver;
 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
+import org.apache.archiva.metadata.repository.MetadataService;
 import org.apache.archiva.metadata.repository.MetadataSessionException;
 import org.apache.archiva.metadata.repository.RepositorySession;
 import org.apache.jackrabbit.oak.segment.file.InvalidFileStoreVersionException;
@@ -32,21 +33,15 @@ import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
-import javax.jcr.Node;
-import javax.jcr.NodeIterator;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
-import javax.jcr.query.QueryResult;
-import javax.jcr.query.Row;
-import javax.jcr.query.RowIterator;
 import java.io.IOException;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Collection;
-import java.util.Map;
+import java.util.List;
 
-import static org.apache.archiva.metadata.repository.jcr.JcrConstants.PROJECT_VERSION_NODE_TYPE;
 import static org.assertj.core.api.Assertions.assertThat;
 
 /**
@@ -81,10 +76,12 @@ public class JcrMetadataRepositoryTest
             org.apache.archiva.common.utils.FileUtils.deleteDirectory( directory );
         }
 
-        Map<String, MetadataFacetFactory> factories = createTestMetadataFacetFactories( );
+        List<MetadataFacetFactory> factories = createTestMetadataFacetFactories( );
+        MetadataService metadataService = new MetadataService();
+        metadataService.setMetadataFacetFactories( factories );
         JcrRepositorySessionFactory jcrSessionFactory = new JcrRepositorySessionFactory( );
         jcrSessionFactory.setMetadataResolver( new DefaultMetadataResolver( ) );
-        jcrSessionFactory.setMetadataFacetFactories( factories );
+        jcrSessionFactory.setMetadataService( metadataService );
 
         jcrSessionFactory.open( );
         sessionFactory = jcrSessionFactory;
diff --git a/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/stats/JcrRepositoryStatisticsGatheringTest.java b/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/stats/JcrRepositoryStatisticsGatheringTest.java
index 6fe7da2..7e1e83d 100644
--- a/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/stats/JcrRepositoryStatisticsGatheringTest.java
+++ b/archiva-modules/plugins/metadata-store-jcr/src/test/java/org/apache/archiva/metadata/repository/stats/JcrRepositoryStatisticsGatheringTest.java
@@ -24,6 +24,7 @@ import org.apache.archiva.metadata.model.MetadataFacetFactory;
 import org.apache.archiva.metadata.repository.AbstractMetadataRepositoryTest;
 import org.apache.archiva.metadata.repository.DefaultMetadataResolver;
 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
+import org.apache.archiva.metadata.repository.MetadataService;
 import org.apache.archiva.metadata.repository.RepositorySession;
 import org.apache.archiva.metadata.repository.jcr.JcrMetadataRepository;
 import org.apache.archiva.metadata.repository.jcr.JcrRepositorySessionFactory;
@@ -54,6 +55,7 @@ import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.List;
 import java.util.Map;
 import java.util.zip.GZIPInputStream;
 
@@ -91,12 +93,14 @@ public class JcrRepositoryStatisticsGatheringTest
             org.apache.archiva.common.utils.FileUtils.deleteDirectory( directory );
         }
 
-        Map<String, MetadataFacetFactory> factories = AbstractMetadataRepositoryTest.createTestMetadataFacetFactories();
+        List<MetadataFacetFactory> factories = AbstractMetadataRepositoryTest.createTestMetadataFacetFactories();
 
+        MetadataService metadataService = new MetadataService( );
+        metadataService.setMetadataFacetFactories( factories );
 
         JcrRepositorySessionFactory jcrSessionFactory = new JcrRepositorySessionFactory();
         jcrSessionFactory.setMetadataResolver(new DefaultMetadataResolver());
-        jcrSessionFactory.setMetadataFacetFactories(factories);
+        jcrSessionFactory.setMetadataService(metadataService);
 
         jcrSessionFactory.open();
         sessionFactory = jcrSessionFactory;
diff --git a/archiva-modules/plugins/problem-reports/src/main/java/org/apache/archiva/reports/RepositoryProblemFacetFactory.java b/archiva-modules/plugins/problem-reports/src/main/java/org/apache/archiva/reports/RepositoryProblemFacetFactory.java
index e2ca9c5..44001b7 100644
--- a/archiva-modules/plugins/problem-reports/src/main/java/org/apache/archiva/reports/RepositoryProblemFacetFactory.java
+++ b/archiva-modules/plugins/problem-reports/src/main/java/org/apache/archiva/reports/RepositoryProblemFacetFactory.java
@@ -19,8 +19,7 @@ package org.apache.archiva.reports;
  * under the License.
  */
 
-import org.apache.archiva.metadata.model.MetadataFacet;
-import org.apache.archiva.metadata.model.MetadataFacetFactory;
+import org.apache.archiva.metadata.model.facets.AbstractMetadataFacetFactory;
 import org.apache.archiva.metadata.model.facets.RepositoryProblemFacet;
 import org.springframework.stereotype.Service;
 
@@ -29,16 +28,21 @@ import org.springframework.stereotype.Service;
  */
 @Service( "metadataFacetFactory#org.apache.archiva.reports" )
 public class RepositoryProblemFacetFactory
-    implements MetadataFacetFactory
+    extends AbstractMetadataFacetFactory<RepositoryProblemFacet>
 {
+    protected RepositoryProblemFacetFactory( )
+    {
+        super( RepositoryProblemFacet.class );
+    }
+
     @Override
-    public MetadataFacet createMetadataFacet()
+    public RepositoryProblemFacet createMetadataFacet()
     {
         return new RepositoryProblemFacet();
     }
 
     @Override
-    public MetadataFacet createMetadataFacet( String repositoryId, String name )
+    public RepositoryProblemFacet createMetadataFacet( String repositoryId, String name )
     {
         return new RepositoryProblemFacet();
     }
diff --git a/archiva-modules/plugins/repository-statistics/src/main/java/org/apache/archiva/metadata/repository/stats/RepositoryStatisticsFactory.java b/archiva-modules/plugins/repository-statistics/src/main/java/org/apache/archiva/metadata/repository/stats/RepositoryStatisticsFactory.java
index ceb9118..beae25d 100644
--- a/archiva-modules/plugins/repository-statistics/src/main/java/org/apache/archiva/metadata/repository/stats/RepositoryStatisticsFactory.java
+++ b/archiva-modules/plugins/repository-statistics/src/main/java/org/apache/archiva/metadata/repository/stats/RepositoryStatisticsFactory.java
@@ -19,9 +19,9 @@ package org.apache.archiva.metadata.repository.stats;
  * under the License.
  */
 
-import org.apache.archiva.metadata.model.MetadataFacet;
-import org.apache.archiva.metadata.model.MetadataFacetFactory;
+import org.apache.archiva.metadata.model.facets.AbstractMetadataFacetFactory;
 import org.apache.archiva.metadata.repository.stats.model.DefaultRepositoryStatistics;
+import org.apache.archiva.metadata.repository.stats.model.RepositoryStatistics;
 import org.springframework.stereotype.Service;
 
 /**
@@ -29,16 +29,21 @@ import org.springframework.stereotype.Service;
  */
 @Service( "metadataFacetFactory#org.apache.archiva.metadata.repository.stats" )
 public class RepositoryStatisticsFactory
-    implements MetadataFacetFactory
+    extends AbstractMetadataFacetFactory<RepositoryStatistics>
 {
+    protected RepositoryStatisticsFactory( )
+    {
+        super( RepositoryStatistics.class );
+    }
+
     @Override
-    public MetadataFacet createMetadataFacet()
+    public RepositoryStatistics createMetadataFacet()
     {
         return new DefaultRepositoryStatistics();
     }
 
     @Override
-    public MetadataFacet createMetadataFacet( String repositoryId, String name )
+    public RepositoryStatistics createMetadataFacet( String repositoryId, String name )
     {
         return new DefaultRepositoryStatistics();
     }