diff --git a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/DefaultMetadataResolver.java b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/DefaultMetadataResolver.java index 8e54a6ea2..09b3fd93f 100644 --- a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/DefaultMetadataResolver.java +++ b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/DefaultMetadataResolver.java @@ -104,29 +104,57 @@ public class DefaultMetadataResolver public Collection getRootNamespaces( String repoId ) { - // TODO: is this assumption correct? could a storage mech. actually know all references in a non-Maven scenario? - // not passed to the storage mechanism as resolving references would require iterating all groups - return metadataRepository.getRootNamespaces( repoId ); + Collection rootNamespaces = metadataRepository.getRootNamespaces( repoId ); + + // TODO: may want caching on this + Collection storageRootNamespaces = storageResolver.getRootNamespaces( repoId ); + if ( storageRootNamespaces != null && !storageRootNamespaces.equals( rootNamespaces ) ) + { + // TODO: update the metadata repository + rootNamespaces = storageRootNamespaces; + } + + return rootNamespaces; } public Collection getNamespaces( String repoId, String namespace ) { - // TODO: is this assumption correct? could a storage mech. actually know all references in a non-Maven scenario? - // not passed to the storage mechanism as resolving references would require iterating all groups - return metadataRepository.getNamespaces( repoId, namespace ); + Collection namespaces = metadataRepository.getNamespaces( repoId, namespace ); + // TODO: may want caching on this + Collection storageNamespaces = storageResolver.getNamespaces( repoId, namespace ); + if ( storageNamespaces != null && !storageNamespaces.equals( namespaces ) ) + { + // TODO: update the metadata repository + namespaces = storageNamespaces; + } + + return namespaces; } public Collection getProjects( String repoId, String namespace ) { - // TODO: is this assumption correct? could a storage mech. actually know all references in a non-Maven scenario? - // not passed to the storage mechanism as resolving references would require iterating all projects - return metadataRepository.getProjects( repoId, namespace ); + Collection projects = metadataRepository.getProjects( repoId, namespace ); + // TODO: may want caching on this + Collection storageProjects = storageResolver.getProjects( repoId, namespace ); + if ( storageProjects != null && !storageProjects.equals( projects ) ) + { + // TODO: update the metadata repository + projects = storageProjects; + } + + return projects; } public Collection getProjectVersions( String repoId, String namespace, String projectId ) { - // TODO: is this assumption correct? could a storage mech. actually know all references in a non-Maven scenario? - // not passed to the storage mechanism as resolving references would require iterating all versions - return metadataRepository.getProjectVersions( repoId, namespace, projectId ); + Collection projectVersions = metadataRepository.getProjectVersions( repoId, namespace, projectId ); + // TODO: may want caching on this + Collection storageProjectVersions = storageResolver.getProjectVersions( repoId, namespace, projectId ); + if ( storageProjectVersions != null && !storageProjectVersions.equals( projectVersions ) ) + { + // TODO: update the metadata repository + projectVersions = storageProjectVersions; + } + return projectVersions; } } diff --git a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/storage/RepositoryPathTranslator.java b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/storage/RepositoryPathTranslator.java index f7a50e7d5..196cb516f 100644 --- a/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/storage/RepositoryPathTranslator.java +++ b/archiva-modules/metadata/metadata-repository-api/src/main/java/org/apache/archiva/metadata/repository/storage/RepositoryPathTranslator.java @@ -26,4 +26,8 @@ public interface RepositoryPathTranslator File toFile( File basedir, String namespace, String projectId, String projectVersion, String filename ); String toPath( String namespace, String projectId, String projectVersion, String filename ); + + File toFile( File basedir, String namespace, String projectId ); + + File toFile( File basedir, String namespace ); } diff --git a/archiva-modules/plugins/maven2-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/Maven2RepositoryMetadataResolver.java b/archiva-modules/plugins/maven2-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/Maven2RepositoryMetadataResolver.java index 771128ff4..d7dac2985 100644 --- a/archiva-modules/plugins/maven2-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/Maven2RepositoryMetadataResolver.java +++ b/archiva-modules/plugins/maven2-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/Maven2RepositoryMetadataResolver.java @@ -20,8 +20,11 @@ package org.apache.archiva.metadata.repository.storage.maven2; */ import java.io.File; +import java.io.FilenameFilter; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import org.apache.archiva.metadata.model.ProjectMetadata; @@ -72,6 +75,24 @@ public class Maven2RepositoryMetadataResolver private final static Logger log = LoggerFactory.getLogger( Maven2RepositoryMetadataResolver.class ); + private static final String METADATA_FILENAME = "maven-metadata.xml"; + + private static final FilenameFilter DIRECTORY_FILTER = new FilenameFilter() + { + public boolean accept( File dir, String name ) + { + if ( name.startsWith( "." ) ) + { + return false; + } + else if ( !new File( dir, name ).isDirectory() ) + { + return false; + } + return true; + } + }; + public ProjectMetadata getProject( String repoId, String namespace, String projectId ) { throw new UnsupportedOperationException(); @@ -90,7 +111,7 @@ public class Maven2RepositoryMetadataResolver if ( VersionUtil.isSnapshot( projectVersion ) ) { File metadataFile = - pathTranslator.toFile( basedir, namespace, projectId, projectVersion, "maven-metadata.xml" ); + pathTranslator.toFile( basedir, namespace, projectId, projectVersion, METADATA_FILENAME ); try { MavenRepositoryMetadata metadata = MavenRepositoryMetadataReader.read( metadataFile ); @@ -285,21 +306,157 @@ public class Maven2RepositoryMetadataResolver public Collection getRootNamespaces( String repoId ) { - throw new UnsupportedOperationException(); + File dir = getRepositoryBasedir( repoId ); + + String[] files = dir.list( DIRECTORY_FILTER ); + return files != null ? Arrays.asList( files ) : Collections.emptyList(); } - public List getNamespaces( String repoId, String namespace ) + private File getRepositoryBasedir( String repoId ) { - throw new UnsupportedOperationException(); + ManagedRepositoryConfiguration repositoryConfiguration = + archivaConfiguration.getConfiguration().findManagedRepositoryById( repoId ); + + return new File( repositoryConfiguration.getLocation() ); + } + + public Collection getNamespaces( String repoId, String namespace ) + { + File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace ); + + // scan all the directories which are potential namespaces. Any directories known to be projects are excluded + Collection namespaces = new ArrayList(); + File[] files = dir.listFiles( DIRECTORY_FILTER ); + if ( files != null ) + { + for ( File file : files ) + { + if ( !isProject( file ) ) + { + namespaces.add( file.getName() ); + } + } + } + return namespaces; } public Collection getProjects( String repoId, String namespace ) { - throw new UnsupportedOperationException(); + File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace ); + + // scan all directories in the namespace, and only include those that are known to be projects + Collection projects = new ArrayList(); + File[] files = dir.listFiles( DIRECTORY_FILTER ); + if ( files != null ) + { + for ( File file : files ) + { + if ( isProject( file ) ) + { + projects.add( file.getName() ); + } + } + } + return projects; } public Collection getProjectVersions( String repoId, String namespace, String projectId ) { - throw new UnsupportedOperationException(); + File dir = pathTranslator.toFile( getRepositoryBasedir( repoId ), namespace, projectId ); + + // all directories in a project directory can be considered a version + Collection projectVersions = new ArrayList(); + String[] files = dir.list( DIRECTORY_FILTER ); + return files != null ? Arrays.asList( files ) : Collections.emptyList(); + } + + private boolean isProject( File dir ) + { + // if a metadata file is present, check if this is the "artifactId" directory, marking it as a project + MavenRepositoryMetadata metadata = readMetadata( dir ); + if ( metadata != null && dir.getName().equals( metadata.getArtifactId() ) ) + { + return true; + } + + // if metadata is missing, scan directories for a valid project version subdirectory, meaning this must be a + // project directory + File[] files = dir.listFiles( DIRECTORY_FILTER ); + if ( files != null ) + { + for ( File file : files ) + { + if ( isProjectVersion( file ) ) + { + return true; + } + } + } + return false; + } + + private boolean isProjectVersion( File dir ) + { + final String artifactId = dir.getParentFile().getName(); + final String projectVersion = dir.getName(); + + // if a metadata file is present, check if this is the "version" directory, marking it as a project version + MavenRepositoryMetadata metadata = readMetadata( dir ); + if ( metadata != null && projectVersion.equals( metadata.getVersion() ) ) + { + return true; + } + + // if metadata is missing, check if there is a POM artifact file to ensure it is a version directory + File[] files; + if ( VersionUtil.isSnapshot( projectVersion ) ) + { + files = dir.listFiles( new FilenameFilter() + { + public boolean accept( File dir, String name ) + { + if ( name.startsWith( artifactId + "-" ) && name.endsWith( ".pom" ) ) + { + String v = name.substring( artifactId.length() + 1, name.length() - 4 ); + v = VersionUtil.getBaseVersion( v ); + if ( v.equals( projectVersion ) ) + { + return true; + } + } + return false; + } + } ); + } + else + { + final String pomFile = artifactId + "-" + projectVersion + ".pom"; + files = dir.listFiles( new FilenameFilter() + { + public boolean accept( File dir, String name ) + { + return pomFile.equals( name ); + } + } ); + } + return files != null && files.length > 0; + } + + private MavenRepositoryMetadata readMetadata( File directory ) + { + MavenRepositoryMetadata metadata = null; + File metadataFile = new File( directory, METADATA_FILENAME ); + if ( metadataFile.exists() ) + { + try + { + metadata = MavenRepositoryMetadataReader.read( metadataFile ); + } + catch ( XMLException e ) + { + // ignore missing or invalid metadata + } + } + return metadata; } } diff --git a/archiva-modules/plugins/maven2-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/Maven2RepositoryPathTranslator.java b/archiva-modules/plugins/maven2-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/Maven2RepositoryPathTranslator.java index 3f5bcc83d..8d352cab9 100644 --- a/archiva-modules/plugins/maven2-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/Maven2RepositoryPathTranslator.java +++ b/archiva-modules/plugins/maven2-repository/src/main/java/org/apache/archiva/metadata/repository/storage/maven2/Maven2RepositoryPathTranslator.java @@ -42,14 +42,52 @@ public class Maven2RepositoryPathTranslator { StringBuilder path = new StringBuilder(); - path.append( formatAsDirectory( namespace ) ).append( PATH_SEPARATOR ); - path.append( projectId ).append( PATH_SEPARATOR ); + appendNamespaceAndProject( path, namespace, projectId ); path.append( projectVersion ).append( PATH_SEPARATOR ); path.append( filename ); return path.toString(); } + public String toPath( String namespace ) + { + StringBuilder path = new StringBuilder(); + + appendNamespace( path, namespace ); + + return path.toString(); + } + + public String toPath( String namespace, String projectId ) + { + StringBuilder path = new StringBuilder(); + + appendNamespaceAndProject( path, namespace, projectId ); + + return path.toString(); + } + + private void appendNamespaceAndProject( StringBuilder path, String namespace, String projectId ) + { + appendNamespace( path, namespace ); + path.append( projectId ).append( PATH_SEPARATOR ); + } + + private void appendNamespace( StringBuilder path, String namespace ) + { + path.append( formatAsDirectory( namespace ) ).append( PATH_SEPARATOR ); + } + + public File toFile( File basedir, String namespace, String projectId ) + { + return new File( basedir, toPath( namespace, projectId ) ); + } + + public File toFile( File basedir, String namespace ) + { + return new File( basedir, toPath( namespace ) ); + } + private String formatAsDirectory( String directory ) { return directory.replace( GROUP_SEPARATOR, PATH_SEPARATOR ); diff --git a/archiva-modules/plugins/maven2-repository/src/test/java/org/apache/archiva/metadata/repository/storage/maven2/Maven2RepositoryMetadataResolverTest.java b/archiva-modules/plugins/maven2-repository/src/test/java/org/apache/archiva/metadata/repository/storage/maven2/Maven2RepositoryMetadataResolverTest.java index 6e95c30e4..3d1414141 100644 --- a/archiva-modules/plugins/maven2-repository/src/test/java/org/apache/archiva/metadata/repository/storage/maven2/Maven2RepositoryMetadataResolverTest.java +++ b/archiva-modules/plugins/maven2-repository/src/test/java/org/apache/archiva/metadata/repository/storage/maven2/Maven2RepositoryMetadataResolverTest.java @@ -220,6 +220,75 @@ public class Maven2RepositoryMetadataResolverTest } + public void testGetRootNamespaces() + { + assertEquals( Arrays.asList( "com", "org" ), resolver.getRootNamespaces( TEST_REPO_ID ) ); + } + + public void testGetNamespaces() + { + assertEquals( Arrays.asList( "example" ), resolver.getNamespaces( TEST_REPO_ID, "com" ) ); + assertEquals( Arrays.asList( "test" ), resolver.getNamespaces( TEST_REPO_ID, "com.example" ) ); + assertEquals( Collections.emptyList(), resolver.getNamespaces( TEST_REPO_ID, "com.example.test" ) ); + + assertEquals( Arrays.asList( "apache" ), resolver.getNamespaces( TEST_REPO_ID, "org" ) ); + assertEquals( Arrays.asList( "archiva", "maven" ), resolver.getNamespaces( TEST_REPO_ID, "org.apache" ) ); + assertEquals( Collections.emptyList(), resolver.getNamespaces( TEST_REPO_ID, "org.apache.archiva" ) ); + assertEquals( Arrays.asList( "plugins", "shared" ), + resolver.getNamespaces( TEST_REPO_ID, "org.apache.maven" ) ); + assertEquals( Collections.emptyList(), + resolver.getNamespaces( TEST_REPO_ID, "org.apache.maven.plugins" ) ); + assertEquals( Collections.emptyList(), + resolver.getNamespaces( TEST_REPO_ID, "org.apache.maven.shared" ) ); + } + + public void testGetProjects() + { + assertEquals( Collections.emptyList(), resolver.getProjects( TEST_REPO_ID, "com" ) ); + assertEquals( Collections.emptyList(), resolver.getProjects( TEST_REPO_ID, "com.example" ) ); + assertEquals( Arrays.asList( "incomplete-metadata", "invalid-pom", "malformed-metadata", "missing-metadata" ), + resolver.getProjects( TEST_REPO_ID, "com.example.test" ) ); + + assertEquals( Collections.emptyList(), resolver.getProjects( TEST_REPO_ID, "org" ) ); + assertEquals( Arrays.asList( "apache" ), resolver.getProjects( TEST_REPO_ID, "org.apache" ) ); + assertEquals( Arrays.asList( "archiva", "archiva-base", "archiva-common", "archiva-modules", "archiva-parent" ), + resolver.getProjects( TEST_REPO_ID, "org.apache.archiva" ) ); + assertEquals( Collections.emptyList(), resolver.getProjects( TEST_REPO_ID, "org.apache.maven" ) ); + assertEquals( Collections.emptyList(), + resolver.getProjects( TEST_REPO_ID, "org.apache.maven.plugins" ) ); + assertEquals( Arrays.asList( "maven-downloader" ), + resolver.getProjects( TEST_REPO_ID, "org.apache.maven.shared" ) ); + } + + public void testGetProjectVersions() + { + assertEquals( Arrays.asList( "1.0-SNAPSHOT" ), + resolver.getProjectVersions( TEST_REPO_ID, "com.example.test", "incomplete-metadata" ) ); + assertEquals( Arrays.asList( "1.0-SNAPSHOT" ), + resolver.getProjectVersions( TEST_REPO_ID, "com.example.test", "malformed-metadata" ) ); + assertEquals( Arrays.asList( "1.0-SNAPSHOT" ), + resolver.getProjectVersions( TEST_REPO_ID, "com.example.test", "missing-metadata" ) ); + assertEquals( Arrays.asList( "1.0" ), + resolver.getProjectVersions( TEST_REPO_ID, "com.example.test", "invalid-pom" ) ); + + assertEquals( Arrays.asList( "4", "5-SNAPSHOT" ), + resolver.getProjectVersions( TEST_REPO_ID, "org.apache", "apache" ) ); + + assertEquals( Arrays.asList( "1.2.1" ), + resolver.getProjectVersions( TEST_REPO_ID, "org.apache.archiva", "archiva" ) ); + assertEquals( Arrays.asList( "1.2.1" ), + resolver.getProjectVersions( TEST_REPO_ID, "org.apache.archiva", "archiva-base" ) ); + assertEquals( Arrays.asList( "1.2.1" ), + resolver.getProjectVersions( TEST_REPO_ID, "org.apache.archiva", "archiva-common" ) ); + assertEquals( Arrays.asList( "1.2.1" ), + resolver.getProjectVersions( TEST_REPO_ID, "org.apache.archiva", "archiva-modules" ) ); + assertEquals( Arrays.asList( "3" ), + resolver.getProjectVersions( TEST_REPO_ID, "org.apache.archiva", "archiva-parent" ) ); + + assertEquals( Collections.emptyList(), + resolver.getProjectVersions( TEST_REPO_ID, "org.apache.maven.shared", "maven-downloader" ) ); + } + private void assertMailingList( MailingList mailingList, String name, String archive, String post, String subscribe, String unsubscribe, List otherArchives, boolean allowPost ) { diff --git a/archiva-modules/plugins/maven2-repository/src/test/repositories/test/.index/some-index.zip b/archiva-modules/plugins/maven2-repository/src/test/repositories/test/.index/some-index.zip new file mode 100644 index 000000000..e69de29bb