1 package org.apache.archiva.rest.services;
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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
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;
56 import javax.inject.Inject;
57 import javax.ws.rs.core.Response;
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;
69 import java.util.jar.JarEntry;
70 import java.util.jar.JarFile;
73 * @author Olivier Lamy
76 @Service( "browseService#rest" )
77 public class DefaultBrowseService
78 extends AbstractRestService
79 implements BrowseService
83 private DependencyTreeBuilder dependencyTreeBuilder;
86 private RepositoryContentFactory repositoryContentFactory;
88 public BrowseResult getRootGroups( String repositoryId )
89 throws ArchivaRestServiceException
91 List<String> selectedRepos = getSelectedRepos( repositoryId );
93 Set<String> namespaces = new LinkedHashSet<String>();
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();
102 MetadataResolver metadataResolver = repositorySession.getResolver();
103 namespacesToCollapse = new LinkedHashSet<String>();
105 for ( String repoId : selectedRepos )
107 namespacesToCollapse.addAll( metadataResolver.resolveRootNamespaces( repositorySession, repoId ) );
109 for ( String n : namespacesToCollapse )
111 // TODO: check performance of this
112 namespaces.add( collapseNamespaces( repositorySession, metadataResolver, selectedRepos, n ) );
115 catch ( MetadataResolutionException e )
117 throw new ArchivaRestServiceException( e.getMessage(),
118 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
122 repositorySession.close();
125 List<BrowseResultEntry> browseGroupResultEntries = new ArrayList<BrowseResultEntry>( namespaces.size() );
126 for ( String namespace : namespaces )
128 browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
131 Collections.sort( browseGroupResultEntries );
132 return new BrowseResult( browseGroupResultEntries );
135 public BrowseResult browseGroupId( String groupId, String repositoryId )
136 throws ArchivaRestServiceException
138 List<String> selectedRepos = getSelectedRepos( repositoryId );
140 Set<String> projects = new LinkedHashSet<String>();
142 RepositorySession repositorySession = repositorySessionFactory.createSession();
143 Set<String> namespaces;
146 MetadataResolver metadataResolver = repositorySession.getResolver();
148 Set<String> namespacesToCollapse = new LinkedHashSet<String>();
149 for ( String repoId : selectedRepos )
151 namespacesToCollapse.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, groupId ) );
153 projects.addAll( metadataResolver.resolveProjects( repositorySession, repoId, groupId ) );
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 )
162 // TODO: check performance of this
164 collapseNamespaces( repositorySession, metadataResolver, selectedRepos, groupId + "." + n ) );
167 catch ( MetadataResolutionException e )
169 throw new ArchivaRestServiceException( e.getMessage(),
170 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
174 repositorySession.close();
176 List<BrowseResultEntry> browseGroupResultEntries =
177 new ArrayList<BrowseResultEntry>( namespaces.size() + projects.size() );
178 for ( String namespace : namespaces )
180 browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
182 for ( String project : projects )
184 browseGroupResultEntries.add( new BrowseResultEntry( groupId + '.' + project, true ) );
186 Collections.sort( browseGroupResultEntries );
187 return new BrowseResult( browseGroupResultEntries );
191 public VersionsList getVersionsList( String groupId, String artifactId, String repositoryId )
192 throws ArchivaRestServiceException
194 List<String> selectedRepos = getSelectedRepos( repositoryId );
198 return new VersionsList( new ArrayList<String>( getVersions( selectedRepos, groupId, artifactId ) ) );
200 catch ( MetadataResolutionException e )
202 throw new ArchivaRestServiceException( e.getMessage(),
203 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
208 private Collection<String> getVersions( List<String> selectedRepos, String groupId, String artifactId )
209 throws MetadataResolutionException
212 RepositorySession repositorySession = repositorySessionFactory.createSession();
215 MetadataResolver metadataResolver = repositorySession.getResolver();
217 Set<String> versions = new LinkedHashSet<String>();
219 for ( String repoId : selectedRepos )
222 metadataResolver.resolveProjectVersions( repositorySession, repoId, groupId, artifactId ) );
225 List<String> sortedVersions = new ArrayList<String>( versions );
227 Collections.sort( sortedVersions, VersionComparator.getInstance() );
229 return sortedVersions;
233 repositorySession.close();
237 public ProjectVersionMetadata getProjectMetadata( String groupId, String artifactId, String version,
238 String repositoryId )
239 throws ArchivaRestServiceException
241 List<String> selectedRepos = getSelectedRepos( repositoryId );
243 RepositorySession repositorySession = null;
246 repositorySession = repositorySessionFactory.createSession();
248 MetadataResolver metadataResolver = repositorySession.getResolver();
250 ProjectVersionMetadata versionMetadata = null;
251 for ( String repoId : selectedRepos )
253 if ( versionMetadata == null || versionMetadata.isIncomplete() )
258 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
261 catch ( MetadataResolutionException e )
264 "Skipping invalid metadata while compiling shared model for " + groupId + ":" + artifactId
265 + " in repo " + repoId + ": " + e.getMessage() );
270 return versionMetadata;
274 if ( repositorySession != null )
276 repositorySession.close();
282 public ProjectVersionMetadata getProjectVersionMetadata( String groupId, String artifactId, String repositoryId )
283 throws ArchivaRestServiceException
286 List<String> selectedRepos = getSelectedRepos( repositoryId );
288 RepositorySession repositorySession = null;
292 Collection<String> projectVersions = getVersions( selectedRepos, groupId, artifactId );
294 repositorySession = repositorySessionFactory.createSession();
296 MetadataResolver metadataResolver = repositorySession.getResolver();
298 ProjectVersionMetadata sharedModel = new ProjectVersionMetadata();
300 MavenProjectFacet mavenFacet = new MavenProjectFacet();
301 mavenFacet.setGroupId( groupId );
302 mavenFacet.setArtifactId( artifactId );
303 sharedModel.addFacet( mavenFacet );
305 boolean isFirstVersion = true;
307 for ( String version : projectVersions )
309 ProjectVersionMetadata versionMetadata = null;
310 for ( String repoId : selectedRepos )
312 if ( versionMetadata == null || versionMetadata.isIncomplete() )
317 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
320 catch ( MetadataResolutionException e )
322 log.error( "Skipping invalid metadata while compiling shared model for " + groupId + ":"
323 + artifactId + " in repo " + repoId + ": " + e.getMessage() );
328 if ( versionMetadata == null )
333 if ( isFirstVersion )
335 sharedModel = versionMetadata;
336 sharedModel.setId( null );
340 MavenProjectFacet versionMetadataMavenFacet =
341 (MavenProjectFacet) versionMetadata.getFacet( MavenProjectFacet.FACET_ID );
342 if ( versionMetadataMavenFacet != null )
344 if ( mavenFacet.getPackaging() != null && !StringUtils.equalsIgnoreCase(
345 mavenFacet.getPackaging(), versionMetadataMavenFacet.getPackaging() ) )
347 mavenFacet.setPackaging( null );
351 if ( StringUtils.isEmpty( sharedModel.getName() ) && !StringUtils.isEmpty(
352 versionMetadata.getName() ) )
354 sharedModel.setName( versionMetadata.getName() );
357 if ( sharedModel.getDescription() != null && !StringUtils.equalsIgnoreCase(
358 sharedModel.getDescription(), versionMetadata.getDescription() ) )
360 sharedModel.setDescription( StringUtils.isNotEmpty( versionMetadata.getDescription() )
361 ? versionMetadata.getDescription()
365 if ( sharedModel.getIssueManagement() != null && versionMetadata.getIssueManagement() != null
366 && !StringUtils.equalsIgnoreCase( sharedModel.getIssueManagement().getUrl(),
367 versionMetadata.getIssueManagement().getUrl() ) )
369 sharedModel.setIssueManagement( versionMetadata.getIssueManagement() );
372 if ( sharedModel.getCiManagement() != null && versionMetadata.getCiManagement() != null
373 && !StringUtils.equalsIgnoreCase( sharedModel.getCiManagement().getUrl(),
374 versionMetadata.getCiManagement().getUrl() ) )
376 sharedModel.setCiManagement( versionMetadata.getCiManagement() );
379 if ( sharedModel.getOrganization() != null && versionMetadata.getOrganization() != null
380 && !StringUtils.equalsIgnoreCase( sharedModel.getOrganization().getName(),
381 versionMetadata.getOrganization().getName() ) )
383 sharedModel.setOrganization( versionMetadata.getOrganization() );
386 if ( sharedModel.getUrl() != null && !StringUtils.equalsIgnoreCase( sharedModel.getUrl(),
387 versionMetadata.getUrl() ) )
389 sharedModel.setUrl( versionMetadata.getUrl() );
393 isFirstVersion = false;
397 catch ( MetadataResolutionException e )
399 throw new ArchivaRestServiceException( e.getMessage(),
400 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
404 if ( repositorySession != null )
406 repositorySession.close();
411 public List<TreeEntry> getTreeEntries( String groupId, String artifactId, String version, String repositoryId )
412 throws ArchivaRestServiceException
414 List<String> selectedRepos = getSelectedRepos( repositoryId );
416 List<TreeEntry> treeEntries = new ArrayList<TreeEntry>();
417 TreeDependencyNodeVisitor treeDependencyNodeVisitor = new TreeDependencyNodeVisitor( treeEntries );
420 dependencyTreeBuilder.buildDependencyTree( selectedRepos, groupId, artifactId, version,
421 treeDependencyNodeVisitor );
423 catch ( DependencyTreeBuilderException e )
425 throw new ArchivaRestServiceException( e.getMessage(),
426 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
431 public List<ManagedRepository> getUserRepositories()
432 throws ArchivaRestServiceException
436 return userRepositories.getAccessibleRepositories( getPrincipal() );
438 catch ( ArchivaSecurityException e )
440 throw new ArchivaRestServiceException( "repositories.read.observable.error",
441 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
445 public List<Artifact> getDependees( String groupId, String artifactId, String version, String repositoryId )
446 throws ArchivaRestServiceException
448 List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
449 // TODO: what if we get duplicates across repositories?
450 RepositorySession repositorySession = repositorySessionFactory.createSession();
453 MetadataResolver metadataResolver = repositorySession.getResolver();
454 for ( String repoId : getObservableRepos() )
456 // TODO: what about if we want to see this irrespective of version?
458 metadataResolver.resolveProjectReferences( repositorySession, repoId, groupId, artifactId,
462 catch ( MetadataResolutionException e )
464 throw new ArchivaRestServiceException( e.getMessage(),
465 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
469 repositorySession.close();
472 List<Artifact> artifacts = new ArrayList<Artifact>( references.size() );
474 for ( ProjectVersionReference projectVersionReference : references )
476 artifacts.add( new Artifact( projectVersionReference.getNamespace(), projectVersionReference.getProjectId(),
477 projectVersionReference.getProjectVersion() ) );
482 public List<Entry> getMetadatas( String groupId, String artifactId, String version, String repositoryId )
483 throws ArchivaRestServiceException
485 ProjectVersionMetadata projectVersionMetadata =
486 getProjectMetadata( groupId, artifactId, version, repositoryId );
487 if ( projectVersionMetadata == null )
489 return Collections.emptyList();
491 MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
493 if ( metadataFacet == null )
495 return Collections.emptyList();
497 Map<String, String> map = metadataFacet.toProperties();
498 List<Entry> entries = new ArrayList<Entry>( map.size() );
500 for ( Map.Entry<String, String> entry : map.entrySet() )
502 entries.add( new Entry( entry.getKey(), entry.getValue() ) );
508 public Boolean addMetadata( String groupId, String artifactId, String version, String key, String value,
509 String repositoryId )
510 throws ArchivaRestServiceException
512 ProjectVersionMetadata projectVersionMetadata =
513 getProjectMetadata( groupId, artifactId, version, repositoryId );
515 if ( projectVersionMetadata == null )
517 return Boolean.FALSE;
520 Map<String, String> properties = new HashMap<String, String>();
522 MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
524 if ( metadataFacet != null && metadataFacet.toProperties() != null )
526 properties.putAll( metadataFacet.toProperties() );
530 metadataFacet = new GenericMetadataFacet();
533 properties.put( key, value );
535 metadataFacet.fromProperties( properties );
537 projectVersionMetadata.addFacet( metadataFacet );
539 RepositorySession repositorySession = repositorySessionFactory.createSession();
543 MetadataRepository metadataRepository = repositorySession.getRepository();
545 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
547 repositorySession.save();
549 catch ( MetadataRepositoryException e )
551 log.error( e.getMessage(), e );
552 throw new ArchivaRestServiceException( e.getMessage(),
553 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
557 repositorySession.close();
562 public Boolean deleteMetadata( String groupId, String artifactId, String version, String key, String repositoryId )
563 throws ArchivaRestServiceException
565 ProjectVersionMetadata projectVersionMetadata =
566 getProjectMetadata( groupId, artifactId, version, repositoryId );
568 if ( projectVersionMetadata == null )
570 return Boolean.FALSE;
573 GenericMetadataFacet metadataFacet =
574 (GenericMetadataFacet) projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
576 if ( metadataFacet != null && metadataFacet.toProperties() != null )
578 Map<String, String> properties = metadataFacet.toProperties();
579 properties.remove( key );
580 metadataFacet.setAdditionalProperties( properties );
587 RepositorySession repositorySession = repositorySessionFactory.createSession();
591 MetadataRepository metadataRepository = repositorySession.getRepository();
593 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
595 repositorySession.save();
597 catch ( MetadataRepositoryException e )
599 log.error( e.getMessage(), e );
600 throw new ArchivaRestServiceException( e.getMessage(),
601 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
605 repositorySession.close();
610 public List<ArtifactContentEntry> getArtifactContentEntries( String groupId, String artifactId, String version,
611 String classifier, String type, String path,
612 String repositoryId )
613 throws ArchivaRestServiceException
615 List<String> selectedRepos = getSelectedRepos( repositoryId );
618 for ( String repoId : selectedRepos )
621 ManagedRepositoryContent managedRepositoryContent =
622 repositoryContentFactory.getManagedRepositoryContent( repoId );
623 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, classifier,
624 StringUtils.isEmpty( type ) ? "jar" : type,
626 File file = managedRepositoryContent.toFile( archivaArtifact );
629 return readFileEntries( file, path );
633 catch ( IOException e )
635 log.error( e.getMessage(), e );
636 throw new ArchivaRestServiceException( e.getMessage(),
637 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
639 catch ( RepositoryNotFoundException e )
641 log.error( e.getMessage(), e );
642 throw new ArchivaRestServiceException( e.getMessage(),
643 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
645 catch ( RepositoryException e )
647 log.error( e.getMessage(), e );
648 throw new ArchivaRestServiceException( e.getMessage(),
649 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
651 return Collections.emptyList();
654 //---------------------------
656 //---------------------------
658 protected List<ArtifactContentEntry> readFileEntries( File file, String filterPath )
661 Map<String, ArtifactContentEntry> artifactContentEntryMap = new HashMap<String, ArtifactContentEntry>();
662 int filterDepth = StringUtils.countMatches( filterPath, "/" );
663 /*if ( filterDepth == 0 )
667 JarFile jarFile = new JarFile( file );
670 Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
671 while ( jarEntryEnumeration.hasMoreElements() )
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 )
683 artifactContentEntryMap.put( entryRootPath,
684 new ArtifactContentEntry( entryRootPath, !currentEntry.isDirectory(),
689 if ( StringUtils.startsWith( cleanedEntryName, filterPath ) && ( depth == filterDepth || (
690 !currentEntry.isDirectory() && depth == filterDepth ) ) )
692 artifactContentEntryMap.put( cleanedEntryName, new ArtifactContentEntry( cleanedEntryName,
693 !currentEntry.isDirectory(),
699 if ( StringUtils.isNotEmpty( filterPath ) )
701 Map<String, ArtifactContentEntry> filteredArtifactContentEntryMap =
702 new HashMap<String, ArtifactContentEntry>();
704 for ( Map.Entry<String, ArtifactContentEntry> entry : artifactContentEntryMap.entrySet() )
706 filteredArtifactContentEntryMap.put( entry.getKey(), entry.getValue() );
709 List<ArtifactContentEntry> sorted = getSmallerDepthEntries( filteredArtifactContentEntryMap );
710 if ( sorted == null )
712 return Collections.emptyList();
714 Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
720 if ( jarFile != null )
725 List<ArtifactContentEntry> sorted = new ArrayList<ArtifactContentEntry>( artifactContentEntryMap.values() );
726 Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
730 private List<ArtifactContentEntry> getSmallerDepthEntries( Map<String, ArtifactContentEntry> entries )
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() )
737 ArtifactContentEntry current = entry.getValue();
739 if ( current.getDepth() < smallestDepth )
741 smallestDepth = current.getDepth();
744 List<ArtifactContentEntry> currentList = perDepthList.get( current.getDepth() );
746 if ( currentList == null )
748 currentList = new ArrayList<ArtifactContentEntry>();
749 currentList.add( current );
750 perDepthList.put( current.getDepth(), currentList );
754 currentList.add( current );
759 return perDepthList.get( smallestDepth );
764 * @return org/apache -> org , org -> org
766 private String getRootPath( String path )
768 if ( StringUtils.contains( path, '/' ) )
770 return StringUtils.substringBefore( path, "/" );
775 private List<String> getSelectedRepos( String repositoryId )
776 throws ArchivaRestServiceException
779 List<String> selectedRepos = getObservableRepos();
781 if ( CollectionUtils.isEmpty( selectedRepos ) )
787 if ( StringUtils.isNotEmpty( repositoryId ) )
789 // check user has karma on the repository
790 if ( !selectedRepos.contains( repositoryId ) )
792 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
793 Response.Status.FORBIDDEN.getStatusCode(), null );
795 selectedRepos = Collections.singletonList( repositoryId );
797 return selectedRepos;
801 private String collapseNamespaces( RepositorySession repositorySession, MetadataResolver metadataResolver,
802 Collection<String> repoIds, String n )
803 throws MetadataResolutionException
805 Set<String> subNamespaces = new LinkedHashSet<String>();
806 for ( String repoId : repoIds )
808 subNamespaces.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, n ) );
810 if ( subNamespaces.size() != 1 )
812 log.debug( "{} is not collapsible as it has sub-namespaces: {}", n, subNamespaces );
817 for ( String repoId : repoIds )
819 Collection<String> projects = metadataResolver.resolveProjects( repositorySession, repoId, n );
820 if ( projects != null && !projects.isEmpty() )
822 log.debug( "{} is not collapsible as it has projects", n );
826 return collapseNamespaces( repositorySession, metadataResolver, repoIds,
827 n + "." + subNamespaces.iterator().next() );