aboutsummaryrefslogtreecommitdiffstats
path: root/archiva-modules/metadata/metadata-repository-api/src
diff options
context:
space:
mode:
authorMartin Stockhammer <martin_s@apache.org>2019-08-18 20:56:46 +0200
committerMartin Stockhammer <martin_s@apache.org>2019-08-18 20:56:46 +0200
commit6cf4073a9e73b6517c51dc22db75fd97d2523695 (patch)
treef969c1eee474fd298092ec8cbf087c5d8f3e0e90 /archiva-modules/metadata/metadata-repository-api/src
parent118604a706f1fd66bcda450c1ac8370eff60e4cf (diff)
downloadarchiva-6cf4073a9e73b6517c51dc22db75fd97d2523695.tar.gz
archiva-6cf4073a9e73b6517c51dc22db75fd97d2523695.zip
Changing facet factories. Adding stream methods to metadata repository.
Diffstat (limited to 'archiva-modules/metadata/metadata-repository-api/src')
-rw-r--r--archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataRepository.java159
-rw-r--r--archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataResolver.java8
-rw-r--r--archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/MetadataService.java100
-rw-r--r--archiva-modules/metadata/metadata-repository-api/src/test/java/org/apache/archiva/metadata/repository/AbstractMetadataRepositoryTest.java68
4 files changed, 307 insertions, 28 deletions
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 5048353b5..c35d5f937 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 4cd2cfd41..aae2cf079 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 000000000..21c4f04d9
--- /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 <martin_s@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 a5574d635..ea47a00bf 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) );
} );
}
}