]> source.dussan.org Git - archiva.git/commitdiff
[MRM-1620] add a tab to browse artifact content in artifact detail view
authorOlivier Lamy <olamy@apache.org>
Thu, 5 Apr 2012 13:04:10 +0000 (13:04 +0000)
committerOlivier Lamy <olamy@apache.org>
Thu, 5 Apr 2012 13:04:10 +0000 (13:04 +0000)
implement artifact content browsing REST api

git-svn-id: https://svn.apache.org/repos/asf/archiva/trunk@1309830 13f79535-47bb-0310-9956-ffa450edef68

archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/model/ArtifactContentEntry.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-api/src/main/java/org/apache/archiva/rest/api/services/BrowseService.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/main/java/org/apache/archiva/rest/services/DefaultBrowseService.java
archiva-modules/archiva-web/archiva-rest/archiva-rest-services/src/test/java/org/apache/archiva/rest/services/BrowseServiceTest.java
archiva-modules/archiva-web/archiva-webdav/src/main/java/org/apache/archiva/webdav/RepositoryServlet.java

index fac87f2ca1db9dbdcdddbd118b8987697be1bf56..7a9b2ff91fb1dacd4aa933be850c50e34dea63fb 100644 (file)
@@ -31,15 +31,18 @@ public class ArtifactContentEntry
 
     private boolean file;
 
+    private int depth;
+
     public ArtifactContentEntry()
     {
         // no op
     }
 
-    public ArtifactContentEntry( String name, boolean file )
+    public ArtifactContentEntry( String name, boolean file, int depth )
     {
         this.name = name;
         this.file = file;
+        this.depth = depth;
     }
 
     public String getName()
@@ -62,6 +65,16 @@ public class ArtifactContentEntry
         this.file = file;
     }
 
+    public int getDepth()
+    {
+        return depth;
+    }
+
+    public void setDepth( int depth )
+    {
+        this.depth = depth;
+    }
+
     @Override
     public String toString()
     {
@@ -69,7 +82,47 @@ public class ArtifactContentEntry
         sb.append( "ArtifactContentEntry" );
         sb.append( "{name='" ).append( name ).append( '\'' );
         sb.append( ", file=" ).append( file );
+        sb.append( ", depth=" ).append( depth );
         sb.append( '}' );
         return sb.toString();
     }
+
+    @Override
+    public boolean equals( Object o )
+    {
+        if ( this == o )
+        {
+            return true;
+        }
+        if ( !( o instanceof ArtifactContentEntry ) )
+        {
+            return false;
+        }
+
+        ArtifactContentEntry that = (ArtifactContentEntry) o;
+
+        if ( depth != that.depth )
+        {
+            return false;
+        }
+        if ( file != that.file )
+        {
+            return false;
+        }
+        if ( !name.equals( that.name ) )
+        {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode()
+    {
+        int result = name.hashCode();
+        result = 31 * result + ( file ? 1 : 0 );
+        result = 31 * result + depth;
+        return result;
+    }
 }
index ff628931b9f02d4737c3d12b474878a9e2f9c017..0afa9dbc28bb071e66d8529e5c7e2c6150a87ef5 100644 (file)
@@ -146,10 +146,16 @@ public interface BrowseService
                             @QueryParam( "repositoryId" ) String repositoryId )
         throws ArchivaRestServiceException;
 
+    @Path( "artifactContentEntries/{g}/{a}/{v}" )
+    @GET
+    @Produces( { MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML } )
+    @RedbackAuthorization( noPermission = true, noRestriction = true )
     List<ArtifactContentEntry> getArtifactContentEntries( @PathParam( "g" ) String groupId,
                                                           @PathParam( "a" ) String artifactId,
                                                           @PathParam( "v" ) String version,
-                                                          @PathParam( "" ) String path,
+                                                          @QueryParam( "c" ) String classifier,
+                                                          @QueryParam( "t" ) String type,
+                                                          @QueryParam( "p" ) String path,
                                                           @QueryParam( "repositoryId" ) String repositoryId )
         throws ArchivaRestServiceException;
 }
index 8b0b1198309e9f2e3d47b89aa2a1d410516bd78b..85a0da594e1c79843849cbf3742dddc2fb704a6c 100644 (file)
@@ -31,6 +31,11 @@ import org.apache.archiva.metadata.repository.MetadataResolutionException;
 import org.apache.archiva.metadata.repository.MetadataResolver;
 import org.apache.archiva.metadata.repository.RepositorySession;
 import org.apache.archiva.metadata.repository.storage.maven2.MavenProjectFacet;
+import org.apache.archiva.model.ArchivaArtifact;
+import org.apache.archiva.repository.ManagedRepositoryContent;
+import org.apache.archiva.repository.RepositoryContentFactory;
+import org.apache.archiva.repository.RepositoryException;
+import org.apache.archiva.repository.RepositoryNotFoundException;
 import org.apache.archiva.rest.api.model.Artifact;
 import org.apache.archiva.rest.api.model.ArtifactContentEntry;
 import org.apache.archiva.rest.api.model.BrowseResult;
@@ -40,6 +45,7 @@ import org.apache.archiva.rest.api.model.TreeEntry;
 import org.apache.archiva.rest.api.model.VersionsList;
 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
 import org.apache.archiva.rest.api.services.BrowseService;
+import org.apache.archiva.rest.services.utils.ArtifactContentEntryComparator;
 import org.apache.archiva.rest.services.utils.TreeDependencyNodeVisitor;
 import org.apache.archiva.security.ArchivaSecurityException;
 import org.apache.commons.collections.CollectionUtils;
@@ -49,14 +55,19 @@ import org.springframework.stereotype.Service;
 
 import javax.inject.Inject;
 import javax.ws.rs.core.Response;
+import java.io.File;
+import java.io.IOException;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
 
 /**
  * @author Olivier Lamy
@@ -71,6 +82,9 @@ public class DefaultBrowseService
     @Inject
     private DependencyTreeBuilder dependencyTreeBuilder;
 
+    @Inject
+    private RepositoryContentFactory repositoryContentFactory;
+
     public BrowseResult getRootGroups( String repositoryId )
         throws ArchivaRestServiceException
     {
@@ -594,16 +608,173 @@ public class DefaultBrowseService
     }
 
     public List<ArtifactContentEntry> getArtifactContentEntries( String groupId, String artifactId, String version,
-                                                                 String path, String repositoryId )
+                                                                 String classifier, String type, String path,
+                                                                 String repositoryId )
         throws ArchivaRestServiceException
     {
-        return null;
+        List<String> selectedRepos = getSelectedRepos( repositoryId );
+        try
+        {
+            for ( String repoId : selectedRepos )
+            {
+
+                ManagedRepositoryContent managedRepositoryContent =
+                    repositoryContentFactory.getManagedRepositoryContent( repoId );
+                ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, classifier,
+                                                                       StringUtils.isEmpty( type ) ? "jar" : type,
+                                                                       repositoryId );
+                File file = managedRepositoryContent.toFile( archivaArtifact );
+                if ( file.exists() )
+                {
+                    return readFileEntries( file, path );
+                }
+            }
+        }
+        catch ( IOException e )
+        {
+            log.error( e.getMessage(), e );
+            throw new ArchivaRestServiceException( e.getMessage(),
+                                                   Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
+        }
+        catch ( RepositoryNotFoundException e )
+        {
+            log.error( e.getMessage(), e );
+            throw new ArchivaRestServiceException( e.getMessage(),
+                                                   Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
+        }
+        catch ( RepositoryException e )
+        {
+            log.error( e.getMessage(), e );
+            throw new ArchivaRestServiceException( e.getMessage(),
+                                                   Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
+        }
+        return Collections.emptyList();
     }
 
     //---------------------------
     // internals
     //---------------------------
 
+    protected List<ArtifactContentEntry> readFileEntries( File file, String filterPath )
+        throws IOException
+    {
+        Map<String, ArtifactContentEntry> artifactContentEntryMap = new HashMap<String, ArtifactContentEntry>();
+        int filterDepth = StringUtils.countMatches( filterPath, "/" );
+        if ( filterDepth == 0 )
+        {
+            filterDepth = 1;
+        }
+        JarFile jarFile = new JarFile( file );
+        try
+        {
+            Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
+            while ( jarEntryEnumeration.hasMoreElements() )
+            {
+                JarEntry entry = jarEntryEnumeration.nextElement();
+                String entryName = entry.getName();
+                String entryRootPath = getRootPath( entryName );
+                int depth = StringUtils.countMatches( entryName, "/" );
+                if ( StringUtils.isEmpty( filterPath ) && !artifactContentEntryMap.containsKey( entryRootPath ) )
+                {
+
+                    artifactContentEntryMap.put( entryRootPath,
+                                                 new ArtifactContentEntry( entryRootPath, !entry.isDirectory(),
+                                                                           depth ) );
+                }
+                else
+                {
+                    if ( StringUtils.startsWith( entryName, filterPath ) && ( depth > filterDepth || (
+                        !entry.isDirectory() && depth == filterDepth ) ) )
+                    {
+                        // remove last /
+                        String cleanedEntryName = StringUtils.endsWith( entryName, "/" )
+                            ? StringUtils.substringBeforeLast( entryName, "/" )
+                            : entryName;
+                        artifactContentEntryMap.put( cleanedEntryName,
+                                                     new ArtifactContentEntry( cleanedEntryName, !entry.isDirectory(),
+                                                                               depth ) );
+                    }
+                }
+            }
+
+            if ( StringUtils.isNotEmpty( filterPath ) )
+            {
+                // apply more filtering here
+                // search entries filterPath/blabla
+                Map<String, ArtifactContentEntry> filteredArtifactContentEntryMap =
+                    new HashMap<String, ArtifactContentEntry>();
+
+                for ( Map.Entry<String, ArtifactContentEntry> entry : artifactContentEntryMap.entrySet() )
+                {
+                    filteredArtifactContentEntryMap.put( entry.getKey(), entry.getValue() );
+                }
+
+                List<ArtifactContentEntry> sorted = getSmallerDepthEntries( filteredArtifactContentEntryMap );
+                if ( sorted == null )
+                {
+                    return Collections.emptyList();
+                }
+                Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
+                return sorted;
+            }
+        }
+        finally
+        {
+            if ( jarFile != null )
+            {
+                jarFile.close();
+            }
+        }
+        List<ArtifactContentEntry> sorted = new ArrayList<ArtifactContentEntry>( artifactContentEntryMap.values() );
+        Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
+        return sorted;
+    }
+
+    private List<ArtifactContentEntry> getSmallerDepthEntries( Map<String, ArtifactContentEntry> entries )
+    {
+        int smallestDepth = Integer.MAX_VALUE;
+        Map<Integer, List<ArtifactContentEntry>> perDepthList = new HashMap<Integer, List<ArtifactContentEntry>>();
+        for ( Map.Entry<String, ArtifactContentEntry> entry : entries.entrySet() )
+        {
+
+            ArtifactContentEntry current = entry.getValue();
+
+            if ( current.getDepth() < smallestDepth )
+            {
+                smallestDepth = current.getDepth();
+            }
+
+            List<ArtifactContentEntry> currentList = perDepthList.get( current.getDepth() );
+
+            if ( currentList == null )
+            {
+                currentList = new ArrayList<ArtifactContentEntry>();
+                currentList.add( current );
+                perDepthList.put( current.getDepth(), currentList );
+            }
+            else
+            {
+                currentList.add( current );
+            }
+
+        }
+
+        return perDepthList.get( smallestDepth );
+    }
+
+    /**
+     * @param path
+     * @return org/apache -> org , org -> org
+     */
+    private String getRootPath( String path )
+    {
+        if ( StringUtils.contains( path, '/' ) )
+        {
+            return StringUtils.substringBefore( path, "/" );
+        }
+        return path;
+    }
+
     private List<String> getSelectedRepos( String repositoryId )
         throws ArchivaRestServiceException
     {
index cc1bedefea6644709b37cca9258ef6f53a815e1a..854886f8a1e4bf036f54af5b78d658d6c32c8297 100644 (file)
@@ -19,6 +19,7 @@ package org.apache.archiva.rest.services;
  */
 
 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
+import org.apache.archiva.rest.api.model.ArtifactContentEntry;
 import org.apache.archiva.rest.api.model.BrowseResult;
 import org.apache.archiva.rest.api.model.BrowseResultEntry;
 import org.apache.archiva.rest.api.model.Entry;
@@ -222,4 +223,56 @@ public class BrowseServiceTest
 
         deleteTestRepo( testRepoId );
     }
+
+    @Test
+    public void readArtifactContentEntries()
+        throws Exception
+    {
+        String testRepoId = "test-repo";
+        // force guest user creation if not exists
+        if ( getUserService( authorizationHeader ).getGuestUser() == null )
+        {
+            assertNotNull( getUserService( authorizationHeader ).createGuestUser() );
+        }
+
+        createAndIndexRepo( testRepoId, new File( getBasedir(), "src/test/repo-with-osgi" ).getAbsolutePath(), false );
+
+        BrowseService browseService = getBrowseService( authorizationHeader, true );
+
+        List<ArtifactContentEntry> artifactContentEntries =
+            browseService.getArtifactContentEntries( "commons-logging", "commons-logging", "1.1", null, null, null,
+                                                     testRepoId );
+
+        log.info( "artifactContentEntries: {}", artifactContentEntries );
+
+        assertThat( artifactContentEntries ).isNotNull().isNotEmpty().hasSize( 2 ).contains(
+            new ArtifactContentEntry( "org", false, 1 ), new ArtifactContentEntry( "META-INF", false, 1 ) );
+        deleteTestRepo( testRepoId );
+    }
+
+    @Test
+    public void readArtifactContentEntriesRootPath()
+        throws Exception
+    {
+        String testRepoId = "test-repo";
+        // force guest user creation if not exists
+        if ( getUserService( authorizationHeader ).getGuestUser() == null )
+        {
+            assertNotNull( getUserService( authorizationHeader ).createGuestUser() );
+        }
+
+        createAndIndexRepo( testRepoId, new File( getBasedir(), "src/test/repo-with-osgi" ).getAbsolutePath(), false );
+
+        BrowseService browseService = getBrowseService( authorizationHeader, true );
+
+        List<ArtifactContentEntry> artifactContentEntries =
+            browseService.getArtifactContentEntries( "commons-logging", "commons-logging", "1.1", null, null, "org",
+                                                     testRepoId );
+
+        log.info( "artifactContentEntries: {}", artifactContentEntries );
+
+        assertThat( artifactContentEntries ).isNotNull().isNotEmpty().hasSize( 1 ).contains(
+            new ArtifactContentEntry( "org/apache", false, 2 ) );
+        deleteTestRepo( testRepoId );
+    }
 }
index 4b7d31c3018a3435f772b9ab9c2c3ec681ec4d29..c9b6e807eaa058182abcdea8ceb64ac1c17482df 100644 (file)
@@ -22,6 +22,9 @@ package org.apache.archiva.webdav;
 import org.apache.archiva.admin.model.RepositoryAdminException;
 import org.apache.archiva.admin.model.beans.ManagedRepository;
 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
+import org.apache.archiva.configuration.ArchivaConfiguration;
+import org.apache.archiva.configuration.ConfigurationEvent;
+import org.apache.archiva.configuration.ConfigurationListener;
 import org.apache.archiva.security.ServletAuthenticator;
 import org.apache.jackrabbit.webdav.DavException;
 import org.apache.jackrabbit.webdav.DavLocatorFactory;
@@ -35,9 +38,6 @@ import org.apache.jackrabbit.webdav.WebdavRequestImpl;
 import org.apache.jackrabbit.webdav.WebdavResponse;
 import org.apache.jackrabbit.webdav.WebdavResponseImpl;
 import org.apache.jackrabbit.webdav.server.AbstractWebdavServlet;
-import org.apache.archiva.configuration.ArchivaConfiguration;
-import org.apache.archiva.configuration.ConfigurationEvent;
-import org.apache.archiva.configuration.ConfigurationListener;
 import org.codehaus.redback.integration.filter.authentication.HttpAuthenticator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -189,7 +189,7 @@ public class RepositoryServlet
                 if ( !repoDir.mkdirs() )
                 {
                     // Skip invalid directories.
-                    log( "Unable to create missing directory for " + repo.getLocation() );
+                    log.info( "Unable to create missing directory for {}", repo.getLocation() );
                     continue;
                 }
             }