]> source.dussan.org Git - archiva.git/blob
96c71208f72711b479307a435eff5e7e1d61076a
[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.rest.api.model.Artifact;
35 import org.apache.archiva.rest.api.model.BrowseResult;
36 import org.apache.archiva.rest.api.model.BrowseResultEntry;
37 import org.apache.archiva.rest.api.model.TreeEntry;
38 import org.apache.archiva.rest.api.model.VersionsList;
39 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
40 import org.apache.archiva.rest.api.services.BrowseService;
41 import org.apache.archiva.rest.services.utils.TreeDependencyNodeVisitor;
42 import org.apache.archiva.security.ArchivaSecurityException;
43 import org.apache.commons.collections.CollectionUtils;
44 import org.apache.commons.lang.StringUtils;
45 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
46 import org.springframework.stereotype.Service;
47
48 import javax.inject.Inject;
49 import javax.ws.rs.core.Response;
50 import java.util.ArrayList;
51 import java.util.Collection;
52 import java.util.Collections;
53 import java.util.HashMap;
54 import java.util.LinkedHashSet;
55 import java.util.List;
56 import java.util.Map;
57 import java.util.Set;
58
59 /**
60  * @author Olivier Lamy
61  * @since 1.4-M3
62  */
63 @Service( "browseService#rest" )
64 public class DefaultBrowseService
65     extends AbstractRestService
66     implements BrowseService
67 {
68
69     @Inject
70     private DependencyTreeBuilder dependencyTreeBuilder;
71
72     public BrowseResult getRootGroups( String repositoryId )
73         throws ArchivaRestServiceException
74     {
75         List<String> selectedRepos = getObservableRepos();
76         if ( CollectionUtils.isEmpty( selectedRepos ) )
77         {
78             // FIXME 403 ???
79             return new BrowseResult();
80         }
81
82         if ( StringUtils.isNotEmpty( repositoryId ) )
83         {
84             // check user has karma on the repository
85             if ( !selectedRepos.contains( repositoryId ) )
86             {
87                 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
88                                                        Response.Status.FORBIDDEN.getStatusCode() );
89             }
90             selectedRepos = Collections.singletonList( repositoryId );
91         }
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() );
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
139         List<String> selectedRepos = getObservableRepos();
140         if ( CollectionUtils.isEmpty( selectedRepos ) )
141         {
142             // FIXME 403 ???
143             return new BrowseResult();
144         }
145
146         if ( StringUtils.isNotEmpty( repositoryId ) )
147         {
148             // check user has karma on the repository
149             if ( !selectedRepos.contains( repositoryId ) )
150             {
151                 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
152                                                        Response.Status.FORBIDDEN.getStatusCode() );
153             }
154             selectedRepos = Collections.singletonList( repositoryId );
155         }
156
157         Set<String> projects = new LinkedHashSet<String>();
158
159         RepositorySession repositorySession = repositorySessionFactory.createSession();
160         Set<String> namespaces;
161         try
162         {
163             MetadataResolver metadataResolver = repositorySession.getResolver();
164
165             Set<String> namespacesToCollapse = new LinkedHashSet<String>();
166             for ( String repoId : selectedRepos )
167             {
168                 namespacesToCollapse.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, groupId ) );
169
170                 projects.addAll( metadataResolver.resolveProjects( repositorySession, repoId, groupId ) );
171             }
172
173             // TODO: this logic should be optional, particularly remembering we want to keep this code simple
174             // it is located here to avoid the content repository implementation needing to do too much for what
175             // is essentially presentation code
176             namespaces = new LinkedHashSet<String>();
177             for ( String n : namespacesToCollapse )
178             {
179                 // TODO: check performance of this
180                 namespaces.add(
181                     collapseNamespaces( repositorySession, metadataResolver, selectedRepos, groupId + "." + n ) );
182             }
183         }
184         catch ( MetadataResolutionException e )
185         {
186             throw new ArchivaRestServiceException( e.getMessage(),
187                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
188         }
189         finally
190         {
191             repositorySession.close();
192         }
193         List<BrowseResultEntry> browseGroupResultEntries =
194             new ArrayList<BrowseResultEntry>( namespaces.size() + projects.size() );
195         for ( String namespace : namespaces )
196         {
197             browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
198         }
199         for ( String project : projects )
200         {
201             browseGroupResultEntries.add( new BrowseResultEntry( groupId + '.' + project, true ) );
202         }
203         Collections.sort( browseGroupResultEntries );
204         return new BrowseResult( browseGroupResultEntries );
205
206     }
207
208     public VersionsList getVersionsList( String groupId, String artifactId, String repositoryId )
209         throws ArchivaRestServiceException
210     {
211         List<String> selectedRepos = getObservableRepos();
212         if ( CollectionUtils.isEmpty( selectedRepos ) )
213         {
214             // FIXME 403 ???
215             return new VersionsList();
216         }
217
218         if ( StringUtils.isNotEmpty( repositoryId ) )
219         {
220             // check user has karma on the repository
221             if ( !selectedRepos.contains( repositoryId ) )
222             {
223                 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
224                                                        Response.Status.FORBIDDEN.getStatusCode() );
225             }
226             selectedRepos = Collections.singletonList( repositoryId );
227         }
228
229         try
230         {
231             return new VersionsList( new ArrayList<String>( getVersions( selectedRepos, groupId, artifactId ) ) );
232         }
233         catch ( MetadataResolutionException e )
234         {
235             throw new ArchivaRestServiceException( e.getMessage(),
236                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
237         }
238
239     }
240
241     private Collection<String> getVersions( List<String> selectedRepos, String groupId, String artifactId )
242         throws MetadataResolutionException
243
244     {
245         RepositorySession repositorySession = repositorySessionFactory.createSession();
246         try
247         {
248             MetadataResolver metadataResolver = repositorySession.getResolver();
249
250             Set<String> versions = new LinkedHashSet<String>();
251
252             for ( String repoId : selectedRepos )
253             {
254                 versions.addAll(
255                     metadataResolver.resolveProjectVersions( repositorySession, repoId, groupId, artifactId ) );
256             }
257
258             List<String> sortedVersions = new ArrayList<String>( versions );
259
260             Collections.sort( sortedVersions, VersionComparator.getInstance() );
261
262             return sortedVersions;
263         }
264         finally
265         {
266             repositorySession.close();
267         }
268     }
269
270     public ProjectVersionMetadata getProjectMetadata( String groupId, String artifactId, String version,
271                                                       String repositoryId )
272         throws ArchivaRestServiceException
273     {
274         List<String> selectedRepos = getObservableRepos();
275
276         if ( CollectionUtils.isEmpty( selectedRepos ) )
277         {
278             // FIXME 403 ???
279             return null;
280         }
281
282         if ( StringUtils.isNotEmpty( repositoryId ) )
283         {
284             // check user has karma on the repository
285             if ( !selectedRepos.contains( repositoryId ) )
286             {
287                 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
288                                                        Response.Status.FORBIDDEN.getStatusCode() );
289             }
290             selectedRepos = Collections.singletonList( repositoryId );
291         }
292
293         RepositorySession repositorySession = null;
294         try
295         {
296             repositorySession = repositorySessionFactory.createSession();
297
298             MetadataResolver metadataResolver = repositorySession.getResolver();
299
300             ProjectVersionMetadata versionMetadata = null;
301             for ( String repoId : selectedRepos )
302             {
303                 if ( versionMetadata == null || versionMetadata.isIncomplete() )
304                 {
305                     try
306                     {
307                         versionMetadata =
308                             metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
309                                                                     version );
310                     }
311                     catch ( MetadataResolutionException e )
312                     {
313                         log.error(
314                             "Skipping invalid metadata while compiling shared model for " + groupId + ":" + artifactId
315                                 + " in repo " + repoId + ": " + e.getMessage() );
316                     }
317                 }
318             }
319
320             return versionMetadata;
321         }
322         finally
323         {
324             if ( repositorySession != null )
325             {
326                 repositorySession.close();
327             }
328         }
329
330     }
331
332     public ProjectVersionMetadata getProjectVersionMetadata( String groupId, String artifactId, String repositoryId )
333         throws ArchivaRestServiceException
334     {
335
336         List<String> selectedRepos = getObservableRepos();
337
338         if ( CollectionUtils.isEmpty( selectedRepos ) )
339         {
340             // FIXME 403 ???
341             return null;
342         }
343
344         if ( StringUtils.isNotEmpty( repositoryId ) )
345         {
346             // check user has karma on the repository
347             if ( !selectedRepos.contains( repositoryId ) )
348             {
349                 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
350                                                        Response.Status.FORBIDDEN.getStatusCode() );
351             }
352             selectedRepos = Collections.singletonList( repositoryId );
353         }
354
355         RepositorySession repositorySession = null;
356         try
357         {
358
359             Collection<String> projectVersions = getVersions( selectedRepos, groupId, artifactId );
360
361             repositorySession = repositorySessionFactory.createSession();
362
363             MetadataResolver metadataResolver = repositorySession.getResolver();
364
365             ProjectVersionMetadata sharedModel = new ProjectVersionMetadata();
366
367             MavenProjectFacet mavenFacet = new MavenProjectFacet();
368             mavenFacet.setGroupId( groupId );
369             mavenFacet.setArtifactId( artifactId );
370             sharedModel.addFacet( mavenFacet );
371
372             boolean isFirstVersion = true;
373
374             for ( String version : projectVersions )
375             {
376                 ProjectVersionMetadata versionMetadata = null;
377                 for ( String repoId : selectedRepos )
378                 {
379                     if ( versionMetadata == null || versionMetadata.isIncomplete() )
380                     {
381                         try
382                         {
383                             versionMetadata =
384                                 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
385                                                                         version );
386                         }
387                         catch ( MetadataResolutionException e )
388                         {
389                             log.error( "Skipping invalid metadata while compiling shared model for " + groupId + ":"
390                                            + artifactId + " in repo " + repoId + ": " + e.getMessage() );
391                         }
392                     }
393                 }
394
395                 if ( versionMetadata == null )
396                 {
397                     continue;
398                 }
399
400                 if ( isFirstVersion )
401                 {
402                     sharedModel = versionMetadata;
403                     sharedModel.setId( null );
404                 }
405                 else
406                 {
407                     MavenProjectFacet versionMetadataMavenFacet =
408                         (MavenProjectFacet) versionMetadata.getFacet( MavenProjectFacet.FACET_ID );
409                     if ( versionMetadataMavenFacet != null )
410                     {
411                         if ( mavenFacet.getPackaging() != null && !StringUtils.equalsIgnoreCase(
412                             mavenFacet.getPackaging(), versionMetadataMavenFacet.getPackaging() ) )
413                         {
414                             mavenFacet.setPackaging( null );
415                         }
416                     }
417
418                     if ( StringUtils.isEmpty( sharedModel.getName() ) && !StringUtils.isEmpty(
419                         versionMetadata.getName() ) )
420                     {
421                         sharedModel.setName( versionMetadata.getName() );
422                     }
423
424                     if ( sharedModel.getDescription() != null && !StringUtils.equalsIgnoreCase(
425                         sharedModel.getDescription(), versionMetadata.getDescription() ) )
426                     {
427                         sharedModel.setDescription( StringUtils.isNotEmpty( versionMetadata.getDescription() )
428                                                         ? versionMetadata.getDescription()
429                                                         : "" );
430                     }
431
432                     if ( sharedModel.getIssueManagement() != null && versionMetadata.getIssueManagement() != null
433                         && !StringUtils.equalsIgnoreCase( sharedModel.getIssueManagement().getUrl(),
434                                                           versionMetadata.getIssueManagement().getUrl() ) )
435                     {
436                         sharedModel.setIssueManagement( versionMetadata.getIssueManagement() );
437                     }
438
439                     if ( sharedModel.getCiManagement() != null && versionMetadata.getCiManagement() != null
440                         && !StringUtils.equalsIgnoreCase( sharedModel.getCiManagement().getUrl(),
441                                                           versionMetadata.getCiManagement().getUrl() ) )
442                     {
443                         sharedModel.setCiManagement( versionMetadata.getCiManagement() );
444                     }
445
446                     if ( sharedModel.getOrganization() != null && versionMetadata.getOrganization() != null
447                         && !StringUtils.equalsIgnoreCase( sharedModel.getOrganization().getName(),
448                                                           versionMetadata.getOrganization().getName() ) )
449                     {
450                         sharedModel.setOrganization( versionMetadata.getOrganization() );
451                     }
452
453                     if ( sharedModel.getUrl() != null && !StringUtils.equalsIgnoreCase( sharedModel.getUrl(),
454                                                                                         versionMetadata.getUrl() ) )
455                     {
456                         sharedModel.setUrl( versionMetadata.getUrl() );
457                     }
458                 }
459
460                 isFirstVersion = false;
461             }
462             return sharedModel;
463         }
464         catch ( MetadataResolutionException e )
465         {
466             throw new ArchivaRestServiceException( e.getMessage(),
467                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
468         }
469         finally
470         {
471             if ( repositorySession != null )
472             {
473                 repositorySession.close();
474             }
475         }
476     }
477
478     public List<TreeEntry> getTreeEntries( String groupId, String artifactId, String version, String repositoryId )
479         throws ArchivaRestServiceException
480     {
481         List<String> selectedRepos = getObservableRepos();
482
483         if ( CollectionUtils.isEmpty( selectedRepos ) )
484         {
485             // FIXME 403 ???
486             return null;
487         }
488
489         if ( StringUtils.isNotEmpty( repositoryId ) )
490         {
491             // check user has karma on the repository
492             if ( !selectedRepos.contains( repositoryId ) )
493             {
494                 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
495                                                        Response.Status.FORBIDDEN.getStatusCode() );
496             }
497             selectedRepos = Collections.singletonList( repositoryId );
498         }
499
500         List<TreeEntry> treeEntries = new ArrayList<TreeEntry>();
501         TreeDependencyNodeVisitor treeDependencyNodeVisitor = new TreeDependencyNodeVisitor( treeEntries );
502         try
503         {
504             dependencyTreeBuilder.buildDependencyTree( selectedRepos, groupId, artifactId, version,
505                                                        treeDependencyNodeVisitor );
506         }
507         catch ( DependencyTreeBuilderException e )
508         {
509             throw new ArchivaRestServiceException( e.getMessage(),
510                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
511         }
512         return treeEntries;
513     }
514
515     public List<ManagedRepository> getUserRepositories()
516         throws ArchivaRestServiceException
517     {
518         try
519         {
520             return userRepositories.getAccessibleRepositories( getPrincipal() );
521         }
522         catch ( ArchivaSecurityException e )
523         {
524             throw new ArchivaRestServiceException( "repositories.read.observable.error",
525                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
526         }
527     }
528
529     public List<Artifact> getDependees( String groupId, String artifactId, String version, String repositoryId )
530         throws ArchivaRestServiceException
531     {
532         List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
533         // TODO: what if we get duplicates across repositories?
534         RepositorySession repositorySession = repositorySessionFactory.createSession();
535         try
536         {
537             MetadataResolver metadataResolver = repositorySession.getResolver();
538             for ( String repoId : getObservableRepos() )
539             {
540                 // TODO: what about if we want to see this irrespective of version?
541                 references.addAll(
542                     metadataResolver.resolveProjectReferences( repositorySession, repoId, groupId, artifactId,
543                                                                version ) );
544             }
545         }
546         catch ( MetadataResolutionException e )
547         {
548             throw new ArchivaRestServiceException( e.getMessage(),
549                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
550         }
551         finally
552         {
553             repositorySession.close();
554         }
555
556         List<Artifact> artifacts = new ArrayList<Artifact>( references.size() );
557
558         for ( ProjectVersionReference projectVersionReference : references )
559         {
560             artifacts.add( new Artifact( projectVersionReference.getNamespace(), projectVersionReference.getProjectId(),
561                                          projectVersionReference.getProjectVersion() ) );
562         }
563         return artifacts;
564     }
565
566     public Map<String, String> getMetadatas( String groupId, String artifactId, String version, String repositoryId )
567         throws ArchivaRestServiceException
568     {
569         ProjectVersionMetadata projectVersionMetadata =
570             getProjectMetadata( groupId, artifactId, version, repositoryId );
571         if ( projectVersionMetadata == null )
572         {
573             return Collections.emptyMap();
574         }
575         MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
576
577         if ( metadataFacet == null )
578         {
579             return Collections.emptyMap();
580         }
581
582         return metadataFacet.toProperties();
583     }
584
585     public Boolean addMetadata( String groupId, String artifactId, String version, String key, String value,
586                                 String repositoryId )
587         throws ArchivaRestServiceException
588     {
589         ProjectVersionMetadata projectVersionMetadata =
590             getProjectMetadata( groupId, artifactId, version, repositoryId );
591
592         if ( projectVersionMetadata == null )
593         {
594             return Boolean.FALSE;
595         }
596
597         Map<String, String> properties = new HashMap<String, String>();
598
599         MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
600
601         if ( metadataFacet != null && metadataFacet.toProperties() != null )
602         {
603             properties.putAll( metadataFacet.toProperties() );
604         }
605         else
606         {
607             metadataFacet = new GenericMetadataFacet();
608         }
609
610         properties.put( key, value );
611
612         metadataFacet.fromProperties( properties );
613
614         projectVersionMetadata.addFacet( metadataFacet );
615
616         RepositorySession repositorySession = repositorySessionFactory.createSession();
617
618         try
619         {
620             MetadataRepository metadataRepository = repositorySession.getRepository();
621
622             metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
623
624             repositorySession.save();
625         }
626         catch ( MetadataRepositoryException e )
627         {
628             log.error( e.getMessage(), e );
629             throw new ArchivaRestServiceException( e.getMessage(),
630                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
631         }
632         finally
633         {
634             repositorySession.close();
635         }
636         return Boolean.TRUE;
637     }
638
639     public Boolean deleteMetadata( String groupId, String artifactId, String version, String key, String repositoryId )
640         throws ArchivaRestServiceException
641     {
642         ProjectVersionMetadata projectVersionMetadata =
643             getProjectMetadata( groupId, artifactId, version, repositoryId );
644
645         if ( projectVersionMetadata == null )
646         {
647             return Boolean.FALSE;
648         }
649
650         GenericMetadataFacet metadataFacet =
651             (GenericMetadataFacet) projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
652
653         if ( metadataFacet != null && metadataFacet.toProperties() != null )
654         {
655             Map<String, String> properties = metadataFacet.toProperties();
656             properties.remove( key );
657             metadataFacet.setAdditionalProperties( properties );
658         }
659         else
660         {
661             return Boolean.TRUE;
662         }
663
664         RepositorySession repositorySession = repositorySessionFactory.createSession();
665
666         try
667         {
668             MetadataRepository metadataRepository = repositorySession.getRepository();
669
670             metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
671
672             repositorySession.save();
673         }
674         catch ( MetadataRepositoryException e )
675         {
676             log.error( e.getMessage(), e );
677             throw new ArchivaRestServiceException( e.getMessage(),
678                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
679         }
680         finally
681         {
682             repositorySession.close();
683         }
684         return Boolean.TRUE;
685     }
686
687     //---------------------------
688     // internals
689     //---------------------------
690
691     private List<String> getSortedList( Set<String> set )
692     {
693         List<String> list = new ArrayList<String>( set );
694         Collections.sort( list );
695         return list;
696     }
697
698     private String collapseNamespaces( RepositorySession repositorySession, MetadataResolver metadataResolver,
699                                        Collection<String> repoIds, String n )
700         throws MetadataResolutionException
701     {
702         Set<String> subNamespaces = new LinkedHashSet<String>();
703         for ( String repoId : repoIds )
704         {
705             subNamespaces.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, n ) );
706         }
707         if ( subNamespaces.size() != 1 )
708         {
709             log.debug( "{} is not collapsible as it has sub-namespaces: {}", n, subNamespaces );
710             return n;
711         }
712         else
713         {
714             for ( String repoId : repoIds )
715             {
716                 Collection<String> projects = metadataResolver.resolveProjects( repositorySession, repoId, n );
717                 if ( projects != null && !projects.isEmpty() )
718                 {
719                     log.debug( "{} is not collapsible as it has projects", n );
720                     return n;
721                 }
722             }
723             return collapseNamespaces( repositorySession, metadataResolver, repoIds,
724                                        n + "." + subNamespaces.iterator().next() );
725         }
726     }
727 }