]> source.dussan.org Git - archiva.git/blob
d94f2923108d248dbf266af7e4e085bbf1ac01e5
[archiva.git] /
1 package org.apache.archiva.rest.services;
2 /*
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  */
20
21 import org.apache.archiva.admin.model.beans.ManagedRepository;
22 import org.apache.archiva.common.utils.VersionComparator;
23 import org.apache.archiva.dependency.tree.maven2.DependencyTreeBuilder;
24 import org.apache.archiva.metadata.generic.GenericMetadataFacet;
25 import org.apache.archiva.metadata.model.MetadataFacet;
26 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
27 import org.apache.archiva.metadata.model.ProjectVersionReference;
28 import org.apache.archiva.metadata.repository.MetadataRepository;
29 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
30 import org.apache.archiva.metadata.repository.MetadataResolutionException;
31 import org.apache.archiva.metadata.repository.MetadataResolver;
32 import org.apache.archiva.metadata.repository.RepositorySession;
33 import org.apache.archiva.metadata.repository.storage.maven2.MavenProjectFacet;
34 import org.apache.archiva.model.ArchivaArtifact;
35 import org.apache.archiva.repository.ManagedRepositoryContent;
36 import org.apache.archiva.repository.RepositoryContentFactory;
37 import org.apache.archiva.repository.RepositoryException;
38 import org.apache.archiva.repository.RepositoryNotFoundException;
39 import org.apache.archiva.rest.api.model.Artifact;
40 import org.apache.archiva.rest.api.model.ArtifactContentEntry;
41 import org.apache.archiva.rest.api.model.BrowseResult;
42 import org.apache.archiva.rest.api.model.BrowseResultEntry;
43 import org.apache.archiva.rest.api.model.Entry;
44 import org.apache.archiva.rest.api.model.TreeEntry;
45 import org.apache.archiva.rest.api.model.VersionsList;
46 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
47 import org.apache.archiva.rest.api.services.BrowseService;
48 import org.apache.archiva.rest.services.utils.ArtifactContentEntryComparator;
49 import org.apache.archiva.rest.services.utils.TreeDependencyNodeVisitor;
50 import org.apache.archiva.security.ArchivaSecurityException;
51 import org.apache.commons.collections.CollectionUtils;
52 import org.apache.commons.lang.StringUtils;
53 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
54 import org.springframework.stereotype.Service;
55
56 import javax.inject.Inject;
57 import javax.ws.rs.core.Response;
58 import java.io.File;
59 import java.io.IOException;
60 import java.util.ArrayList;
61 import java.util.Collection;
62 import java.util.Collections;
63 import java.util.Enumeration;
64 import java.util.HashMap;
65 import java.util.LinkedHashSet;
66 import java.util.List;
67 import java.util.Map;
68 import java.util.Set;
69 import java.util.jar.JarEntry;
70 import java.util.jar.JarFile;
71
72 /**
73  * @author Olivier Lamy
74  * @since 1.4-M3
75  */
76 @Service( "browseService#rest" )
77 public class DefaultBrowseService
78     extends AbstractRestService
79     implements BrowseService
80 {
81
82     @Inject
83     private DependencyTreeBuilder dependencyTreeBuilder;
84
85     @Inject
86     private RepositoryContentFactory repositoryContentFactory;
87
88     public BrowseResult getRootGroups( String repositoryId )
89         throws ArchivaRestServiceException
90     {
91         List<String> selectedRepos = getSelectedRepos( repositoryId );
92
93         Set<String> namespaces = new LinkedHashSet<String>();
94
95         // TODO: this logic should be optional, particularly remembering we want to keep this code simple
96         //       it is located here to avoid the content repository implementation needing to do too much for what
97         //       is essentially presentation code
98         Set<String> namespacesToCollapse;
99         RepositorySession repositorySession = repositorySessionFactory.createSession();
100         try
101         {
102             MetadataResolver metadataResolver = repositorySession.getResolver();
103             namespacesToCollapse = new LinkedHashSet<String>();
104
105             for ( String repoId : selectedRepos )
106             {
107                 namespacesToCollapse.addAll( metadataResolver.resolveRootNamespaces( repositorySession, repoId ) );
108             }
109             for ( String n : namespacesToCollapse )
110             {
111                 // TODO: check performance of this
112                 namespaces.add( collapseNamespaces( repositorySession, metadataResolver, selectedRepos, n ) );
113             }
114         }
115         catch ( MetadataResolutionException e )
116         {
117             throw new ArchivaRestServiceException( e.getMessage(),
118                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
119         }
120         finally
121         {
122             repositorySession.close();
123         }
124
125         List<BrowseResultEntry> browseGroupResultEntries = new ArrayList<BrowseResultEntry>( namespaces.size() );
126         for ( String namespace : namespaces )
127         {
128             browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
129         }
130
131         Collections.sort( browseGroupResultEntries );
132         return new BrowseResult( browseGroupResultEntries );
133     }
134
135     public BrowseResult browseGroupId( String groupId, String repositoryId )
136         throws ArchivaRestServiceException
137     {
138         List<String> selectedRepos = getSelectedRepos( repositoryId );
139
140         Set<String> projects = new LinkedHashSet<String>();
141
142         RepositorySession repositorySession = repositorySessionFactory.createSession();
143         Set<String> namespaces;
144         try
145         {
146             MetadataResolver metadataResolver = repositorySession.getResolver();
147
148             Set<String> namespacesToCollapse = new LinkedHashSet<String>();
149             for ( String repoId : selectedRepos )
150             {
151                 namespacesToCollapse.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, groupId ) );
152
153                 projects.addAll( metadataResolver.resolveProjects( repositorySession, repoId, groupId ) );
154             }
155
156             // TODO: this logic should be optional, particularly remembering we want to keep this code simple
157             // it is located here to avoid the content repository implementation needing to do too much for what
158             // is essentially presentation code
159             namespaces = new LinkedHashSet<String>();
160             for ( String n : namespacesToCollapse )
161             {
162                 // TODO: check performance of this
163                 namespaces.add(
164                     collapseNamespaces( repositorySession, metadataResolver, selectedRepos, groupId + "." + n ) );
165             }
166         }
167         catch ( MetadataResolutionException e )
168         {
169             throw new ArchivaRestServiceException( e.getMessage(),
170                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
171         }
172         finally
173         {
174             repositorySession.close();
175         }
176         List<BrowseResultEntry> browseGroupResultEntries =
177             new ArrayList<BrowseResultEntry>( namespaces.size() + projects.size() );
178         for ( String namespace : namespaces )
179         {
180             browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
181         }
182         for ( String project : projects )
183         {
184             browseGroupResultEntries.add( new BrowseResultEntry( groupId + '.' + project, true ) );
185         }
186         Collections.sort( browseGroupResultEntries );
187         return new BrowseResult( browseGroupResultEntries );
188
189     }
190
191     public VersionsList getVersionsList( String groupId, String artifactId, String repositoryId )
192         throws ArchivaRestServiceException
193     {
194         List<String> selectedRepos = getSelectedRepos( repositoryId );
195
196         try
197         {
198             return new VersionsList( new ArrayList<String>( getVersions( selectedRepos, groupId, artifactId ) ) );
199         }
200         catch ( MetadataResolutionException e )
201         {
202             throw new ArchivaRestServiceException( e.getMessage(),
203                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
204         }
205
206     }
207
208     private Collection<String> getVersions( List<String> selectedRepos, String groupId, String artifactId )
209         throws MetadataResolutionException
210
211     {
212         RepositorySession repositorySession = repositorySessionFactory.createSession();
213         try
214         {
215             MetadataResolver metadataResolver = repositorySession.getResolver();
216
217             Set<String> versions = new LinkedHashSet<String>();
218
219             for ( String repoId : selectedRepos )
220             {
221                 versions.addAll(
222                     metadataResolver.resolveProjectVersions( repositorySession, repoId, groupId, artifactId ) );
223             }
224
225             List<String> sortedVersions = new ArrayList<String>( versions );
226
227             Collections.sort( sortedVersions, VersionComparator.getInstance() );
228
229             return sortedVersions;
230         }
231         finally
232         {
233             repositorySession.close();
234         }
235     }
236
237     public ProjectVersionMetadata getProjectMetadata( String groupId, String artifactId, String version,
238                                                       String repositoryId )
239         throws ArchivaRestServiceException
240     {
241         List<String> selectedRepos = getSelectedRepos( repositoryId );
242
243         RepositorySession repositorySession = null;
244         try
245         {
246             repositorySession = repositorySessionFactory.createSession();
247
248             MetadataResolver metadataResolver = repositorySession.getResolver();
249
250             ProjectVersionMetadata versionMetadata = null;
251             for ( String repoId : selectedRepos )
252             {
253                 if ( versionMetadata == null || versionMetadata.isIncomplete() )
254                 {
255                     try
256                     {
257                         versionMetadata =
258                             metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
259                                                                     version );
260                     }
261                     catch ( MetadataResolutionException e )
262                     {
263                         log.error(
264                             "Skipping invalid metadata while compiling shared model for " + groupId + ":" + artifactId
265                                 + " in repo " + repoId + ": " + e.getMessage() );
266                     }
267                 }
268             }
269
270             return versionMetadata;
271         }
272         finally
273         {
274             if ( repositorySession != null )
275             {
276                 repositorySession.close();
277             }
278         }
279
280     }
281
282     public ProjectVersionMetadata getProjectVersionMetadata( String groupId, String artifactId, String repositoryId )
283         throws ArchivaRestServiceException
284     {
285
286         List<String> selectedRepos = getSelectedRepos( repositoryId );
287
288         RepositorySession repositorySession = null;
289         try
290         {
291
292             Collection<String> projectVersions = getVersions( selectedRepos, groupId, artifactId );
293
294             repositorySession = repositorySessionFactory.createSession();
295
296             MetadataResolver metadataResolver = repositorySession.getResolver();
297
298             ProjectVersionMetadata sharedModel = new ProjectVersionMetadata();
299
300             MavenProjectFacet mavenFacet = new MavenProjectFacet();
301             mavenFacet.setGroupId( groupId );
302             mavenFacet.setArtifactId( artifactId );
303             sharedModel.addFacet( mavenFacet );
304
305             boolean isFirstVersion = true;
306
307             for ( String version : projectVersions )
308             {
309                 ProjectVersionMetadata versionMetadata = null;
310                 for ( String repoId : selectedRepos )
311                 {
312                     if ( versionMetadata == null || versionMetadata.isIncomplete() )
313                     {
314                         try
315                         {
316                             versionMetadata =
317                                 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
318                                                                         version );
319                         }
320                         catch ( MetadataResolutionException e )
321                         {
322                             log.error( "Skipping invalid metadata while compiling shared model for " + groupId + ":"
323                                            + artifactId + " in repo " + repoId + ": " + e.getMessage() );
324                         }
325                     }
326                 }
327
328                 if ( versionMetadata == null )
329                 {
330                     continue;
331                 }
332
333                 if ( isFirstVersion )
334                 {
335                     sharedModel = versionMetadata;
336                     sharedModel.setId( null );
337                 }
338                 else
339                 {
340                     MavenProjectFacet versionMetadataMavenFacet =
341                         (MavenProjectFacet) versionMetadata.getFacet( MavenProjectFacet.FACET_ID );
342                     if ( versionMetadataMavenFacet != null )
343                     {
344                         if ( mavenFacet.getPackaging() != null && !StringUtils.equalsIgnoreCase(
345                             mavenFacet.getPackaging(), versionMetadataMavenFacet.getPackaging() ) )
346                         {
347                             mavenFacet.setPackaging( null );
348                         }
349                     }
350
351                     if ( StringUtils.isEmpty( sharedModel.getName() ) && !StringUtils.isEmpty(
352                         versionMetadata.getName() ) )
353                     {
354                         sharedModel.setName( versionMetadata.getName() );
355                     }
356
357                     if ( sharedModel.getDescription() != null && !StringUtils.equalsIgnoreCase(
358                         sharedModel.getDescription(), versionMetadata.getDescription() ) )
359                     {
360                         sharedModel.setDescription( StringUtils.isNotEmpty( versionMetadata.getDescription() )
361                                                         ? versionMetadata.getDescription()
362                                                         : "" );
363                     }
364
365                     if ( sharedModel.getIssueManagement() != null && versionMetadata.getIssueManagement() != null
366                         && !StringUtils.equalsIgnoreCase( sharedModel.getIssueManagement().getUrl(),
367                                                           versionMetadata.getIssueManagement().getUrl() ) )
368                     {
369                         sharedModel.setIssueManagement( versionMetadata.getIssueManagement() );
370                     }
371
372                     if ( sharedModel.getCiManagement() != null && versionMetadata.getCiManagement() != null
373                         && !StringUtils.equalsIgnoreCase( sharedModel.getCiManagement().getUrl(),
374                                                           versionMetadata.getCiManagement().getUrl() ) )
375                     {
376                         sharedModel.setCiManagement( versionMetadata.getCiManagement() );
377                     }
378
379                     if ( sharedModel.getOrganization() != null && versionMetadata.getOrganization() != null
380                         && !StringUtils.equalsIgnoreCase( sharedModel.getOrganization().getName(),
381                                                           versionMetadata.getOrganization().getName() ) )
382                     {
383                         sharedModel.setOrganization( versionMetadata.getOrganization() );
384                     }
385
386                     if ( sharedModel.getUrl() != null && !StringUtils.equalsIgnoreCase( sharedModel.getUrl(),
387                                                                                         versionMetadata.getUrl() ) )
388                     {
389                         sharedModel.setUrl( versionMetadata.getUrl() );
390                     }
391                 }
392
393                 isFirstVersion = false;
394             }
395             return sharedModel;
396         }
397         catch ( MetadataResolutionException e )
398         {
399             throw new ArchivaRestServiceException( e.getMessage(),
400                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
401         }
402         finally
403         {
404             if ( repositorySession != null )
405             {
406                 repositorySession.close();
407             }
408         }
409     }
410
411     public List<TreeEntry> getTreeEntries( String groupId, String artifactId, String version, String repositoryId )
412         throws ArchivaRestServiceException
413     {
414         List<String> selectedRepos = getSelectedRepos( repositoryId );
415
416         List<TreeEntry> treeEntries = new ArrayList<TreeEntry>();
417         TreeDependencyNodeVisitor treeDependencyNodeVisitor = new TreeDependencyNodeVisitor( treeEntries );
418         try
419         {
420             dependencyTreeBuilder.buildDependencyTree( selectedRepos, groupId, artifactId, version,
421                                                        treeDependencyNodeVisitor );
422         }
423         catch ( DependencyTreeBuilderException e )
424         {
425             throw new ArchivaRestServiceException( e.getMessage(),
426                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
427         }
428         return treeEntries;
429     }
430
431     public List<ManagedRepository> getUserRepositories()
432         throws ArchivaRestServiceException
433     {
434         try
435         {
436             return userRepositories.getAccessibleRepositories( getPrincipal() );
437         }
438         catch ( ArchivaSecurityException e )
439         {
440             throw new ArchivaRestServiceException( "repositories.read.observable.error",
441                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
442         }
443     }
444
445     public List<Artifact> getDependees( String groupId, String artifactId, String version, String repositoryId )
446         throws ArchivaRestServiceException
447     {
448         List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
449         // TODO: what if we get duplicates across repositories?
450         RepositorySession repositorySession = repositorySessionFactory.createSession();
451         try
452         {
453             MetadataResolver metadataResolver = repositorySession.getResolver();
454             for ( String repoId : getObservableRepos() )
455             {
456                 // TODO: what about if we want to see this irrespective of version?
457                 references.addAll(
458                     metadataResolver.resolveProjectReferences( repositorySession, repoId, groupId, artifactId,
459                                                                version ) );
460             }
461         }
462         catch ( MetadataResolutionException e )
463         {
464             throw new ArchivaRestServiceException( e.getMessage(),
465                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
466         }
467         finally
468         {
469             repositorySession.close();
470         }
471
472         List<Artifact> artifacts = new ArrayList<Artifact>( references.size() );
473
474         for ( ProjectVersionReference projectVersionReference : references )
475         {
476             artifacts.add( new Artifact( projectVersionReference.getNamespace(), projectVersionReference.getProjectId(),
477                                          projectVersionReference.getProjectVersion() ) );
478         }
479         return artifacts;
480     }
481
482     public List<Entry> getMetadatas( String groupId, String artifactId, String version, String repositoryId )
483         throws ArchivaRestServiceException
484     {
485         ProjectVersionMetadata projectVersionMetadata =
486             getProjectMetadata( groupId, artifactId, version, repositoryId );
487         if ( projectVersionMetadata == null )
488         {
489             return Collections.emptyList();
490         }
491         MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
492
493         if ( metadataFacet == null )
494         {
495             return Collections.emptyList();
496         }
497         Map<String, String> map = metadataFacet.toProperties();
498         List<Entry> entries = new ArrayList<Entry>( map.size() );
499
500         for ( Map.Entry<String, String> entry : map.entrySet() )
501         {
502             entries.add( new Entry( entry.getKey(), entry.getValue() ) );
503         }
504
505         return entries;
506     }
507
508     public Boolean addMetadata( String groupId, String artifactId, String version, String key, String value,
509                                 String repositoryId )
510         throws ArchivaRestServiceException
511     {
512         ProjectVersionMetadata projectVersionMetadata =
513             getProjectMetadata( groupId, artifactId, version, repositoryId );
514
515         if ( projectVersionMetadata == null )
516         {
517             return Boolean.FALSE;
518         }
519
520         Map<String, String> properties = new HashMap<String, String>();
521
522         MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
523
524         if ( metadataFacet != null && metadataFacet.toProperties() != null )
525         {
526             properties.putAll( metadataFacet.toProperties() );
527         }
528         else
529         {
530             metadataFacet = new GenericMetadataFacet();
531         }
532
533         properties.put( key, value );
534
535         metadataFacet.fromProperties( properties );
536
537         projectVersionMetadata.addFacet( metadataFacet );
538
539         RepositorySession repositorySession = repositorySessionFactory.createSession();
540
541         try
542         {
543             MetadataRepository metadataRepository = repositorySession.getRepository();
544
545             metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
546
547             repositorySession.save();
548         }
549         catch ( MetadataRepositoryException e )
550         {
551             log.error( e.getMessage(), e );
552             throw new ArchivaRestServiceException( e.getMessage(),
553                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
554         }
555         finally
556         {
557             repositorySession.close();
558         }
559         return Boolean.TRUE;
560     }
561
562     public Boolean deleteMetadata( String groupId, String artifactId, String version, String key, String repositoryId )
563         throws ArchivaRestServiceException
564     {
565         ProjectVersionMetadata projectVersionMetadata =
566             getProjectMetadata( groupId, artifactId, version, repositoryId );
567
568         if ( projectVersionMetadata == null )
569         {
570             return Boolean.FALSE;
571         }
572
573         GenericMetadataFacet metadataFacet =
574             (GenericMetadataFacet) projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
575
576         if ( metadataFacet != null && metadataFacet.toProperties() != null )
577         {
578             Map<String, String> properties = metadataFacet.toProperties();
579             properties.remove( key );
580             metadataFacet.setAdditionalProperties( properties );
581         }
582         else
583         {
584             return Boolean.TRUE;
585         }
586
587         RepositorySession repositorySession = repositorySessionFactory.createSession();
588
589         try
590         {
591             MetadataRepository metadataRepository = repositorySession.getRepository();
592
593             metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
594
595             repositorySession.save();
596         }
597         catch ( MetadataRepositoryException e )
598         {
599             log.error( e.getMessage(), e );
600             throw new ArchivaRestServiceException( e.getMessage(),
601                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
602         }
603         finally
604         {
605             repositorySession.close();
606         }
607         return Boolean.TRUE;
608     }
609
610     public List<ArtifactContentEntry> getArtifactContentEntries( String groupId, String artifactId, String version,
611                                                                  String classifier, String type, String path,
612                                                                  String repositoryId )
613         throws ArchivaRestServiceException
614     {
615         List<String> selectedRepos = getSelectedRepos( repositoryId );
616         try
617         {
618             for ( String repoId : selectedRepos )
619             {
620
621                 ManagedRepositoryContent managedRepositoryContent =
622                     repositoryContentFactory.getManagedRepositoryContent( repoId );
623                 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, classifier,
624                                                                        StringUtils.isEmpty( type ) ? "jar" : type,
625                                                                        repositoryId );
626                 File file = managedRepositoryContent.toFile( archivaArtifact );
627                 if ( file.exists() )
628                 {
629                     return readFileEntries( file, path );
630                 }
631             }
632         }
633         catch ( IOException e )
634         {
635             log.error( e.getMessage(), e );
636             throw new ArchivaRestServiceException( e.getMessage(),
637                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
638         }
639         catch ( RepositoryNotFoundException e )
640         {
641             log.error( e.getMessage(), e );
642             throw new ArchivaRestServiceException( e.getMessage(),
643                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
644         }
645         catch ( RepositoryException e )
646         {
647             log.error( e.getMessage(), e );
648             throw new ArchivaRestServiceException( e.getMessage(),
649                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
650         }
651         return Collections.emptyList();
652     }
653
654     //---------------------------
655     // internals
656     //---------------------------
657
658     protected List<ArtifactContentEntry> readFileEntries( File file, String filterPath )
659         throws IOException
660     {
661         Map<String, ArtifactContentEntry> artifactContentEntryMap = new HashMap<String, ArtifactContentEntry>();
662         int filterDepth = StringUtils.countMatches( filterPath, "/" );
663         /*if ( filterDepth == 0 )
664         {
665             filterDepth = 1;
666         }*/
667         JarFile jarFile = new JarFile( file );
668         try
669         {
670             Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
671             while ( jarEntryEnumeration.hasMoreElements() )
672             {
673                 JarEntry currentEntry = jarEntryEnumeration.nextElement();
674                 String cleanedEntryName =
675                     StringUtils.endsWith( currentEntry.getName(), "/" ) ? StringUtils.substringBeforeLast(
676                         currentEntry.getName(), "/" ) : currentEntry.getName();
677                 String entryRootPath = getRootPath( cleanedEntryName );
678                 int depth = StringUtils.countMatches( cleanedEntryName, "/" );
679                 if ( StringUtils.isEmpty( filterPath ) && !artifactContentEntryMap.containsKey( entryRootPath )
680                     && depth == filterDepth )
681                 {
682
683                     artifactContentEntryMap.put( entryRootPath,
684                                                  new ArtifactContentEntry( entryRootPath, !currentEntry.isDirectory(),
685                                                                            depth ) );
686                 }
687                 else
688                 {
689                     if ( StringUtils.startsWith( cleanedEntryName, filterPath ) && ( depth == filterDepth || (
690                         !currentEntry.isDirectory() && depth == filterDepth ) ) )
691                     {
692                         artifactContentEntryMap.put( cleanedEntryName, new ArtifactContentEntry( cleanedEntryName,
693                                                                                                  !currentEntry.isDirectory(),
694                                                                                                  depth ) );
695                     }
696                 }
697             }
698
699             if ( StringUtils.isNotEmpty( filterPath ) )
700             {
701                 Map<String, ArtifactContentEntry> filteredArtifactContentEntryMap =
702                     new HashMap<String, ArtifactContentEntry>();
703
704                 for ( Map.Entry<String, ArtifactContentEntry> entry : artifactContentEntryMap.entrySet() )
705                 {
706                     filteredArtifactContentEntryMap.put( entry.getKey(), entry.getValue() );
707                 }
708
709                 List<ArtifactContentEntry> sorted = getSmallerDepthEntries( filteredArtifactContentEntryMap );
710                 if ( sorted == null )
711                 {
712                     return Collections.emptyList();
713                 }
714                 Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
715                 return sorted;
716             }
717         }
718         finally
719         {
720             if ( jarFile != null )
721             {
722                 jarFile.close();
723             }
724         }
725         List<ArtifactContentEntry> sorted = new ArrayList<ArtifactContentEntry>( artifactContentEntryMap.values() );
726         Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
727         return sorted;
728     }
729
730     private List<ArtifactContentEntry> getSmallerDepthEntries( Map<String, ArtifactContentEntry> entries )
731     {
732         int smallestDepth = Integer.MAX_VALUE;
733         Map<Integer, List<ArtifactContentEntry>> perDepthList = new HashMap<Integer, List<ArtifactContentEntry>>();
734         for ( Map.Entry<String, ArtifactContentEntry> entry : entries.entrySet() )
735         {
736
737             ArtifactContentEntry current = entry.getValue();
738
739             if ( current.getDepth() < smallestDepth )
740             {
741                 smallestDepth = current.getDepth();
742             }
743
744             List<ArtifactContentEntry> currentList = perDepthList.get( current.getDepth() );
745
746             if ( currentList == null )
747             {
748                 currentList = new ArrayList<ArtifactContentEntry>();
749                 currentList.add( current );
750                 perDepthList.put( current.getDepth(), currentList );
751             }
752             else
753             {
754                 currentList.add( current );
755             }
756
757         }
758
759         return perDepthList.get( smallestDepth );
760     }
761
762     /**
763      * @param path
764      * @return org/apache -> org , org -> org
765      */
766     private String getRootPath( String path )
767     {
768         if ( StringUtils.contains( path, '/' ) )
769         {
770             return StringUtils.substringBefore( path, "/" );
771         }
772         return path;
773     }
774
775     private List<String> getSelectedRepos( String repositoryId )
776         throws ArchivaRestServiceException
777     {
778
779         List<String> selectedRepos = getObservableRepos();
780
781         if ( CollectionUtils.isEmpty( selectedRepos ) )
782         {
783             // FIXME 403 ???
784             return null;
785         }
786
787         if ( StringUtils.isNotEmpty( repositoryId ) )
788         {
789             // check user has karma on the repository
790             if ( !selectedRepos.contains( repositoryId ) )
791             {
792                 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
793                                                        Response.Status.FORBIDDEN.getStatusCode(), null );
794             }
795             selectedRepos = Collections.singletonList( repositoryId );
796         }
797         return selectedRepos;
798     }
799
800
801     private String collapseNamespaces( RepositorySession repositorySession, MetadataResolver metadataResolver,
802                                        Collection<String> repoIds, String n )
803         throws MetadataResolutionException
804     {
805         Set<String> subNamespaces = new LinkedHashSet<String>();
806         for ( String repoId : repoIds )
807         {
808             subNamespaces.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, n ) );
809         }
810         if ( subNamespaces.size() != 1 )
811         {
812             log.debug( "{} is not collapsible as it has sub-namespaces: {}", n, subNamespaces );
813             return n;
814         }
815         else
816         {
817             for ( String repoId : repoIds )
818             {
819                 Collection<String> projects = metadataResolver.resolveProjects( repositorySession, repoId, n );
820                 if ( projects != null && !projects.isEmpty() )
821                 {
822                     log.debug( "{} is not collapsible as it has projects", n );
823                     return n;
824                 }
825             }
826             return collapseNamespaces( repositorySession, metadataResolver, repoIds,
827                                        n + "." + subNamespaces.iterator().next() );
828         }
829     }
830 }