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.metadata.model.ProjectVersionMetadata;
24 import org.apache.archiva.metadata.repository.MetadataResolutionException;
25 import org.apache.archiva.metadata.repository.MetadataResolver;
26 import org.apache.archiva.metadata.repository.RepositorySession;
27 import org.apache.archiva.metadata.repository.storage.maven2.MavenProjectFacet;
28 import org.apache.archiva.rest.api.model.BrowseResult;
29 import org.apache.archiva.rest.api.model.BrowseResultEntry;
30 import org.apache.archiva.rest.api.model.VersionsList;
31 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
32 import org.apache.archiva.rest.api.services.BrowseService;
33 import org.apache.archiva.security.ArchivaSecurityException;
34 import org.apache.commons.collections.CollectionUtils;
35 import org.apache.commons.lang.StringUtils;
36 import org.springframework.stereotype.Service;
38 import javax.ws.rs.core.Response;
39 import java.util.ArrayList;
40 import java.util.Collection;
41 import java.util.Collections;
42 import java.util.LinkedHashSet;
43 import java.util.List;
47 * @author Olivier Lamy
50 @Service( "browseService#rest" )
51 public class DefaultBrowseService
52 extends AbstractRestService
53 implements BrowseService
56 public BrowseResult getRootGroups()
57 throws ArchivaRestServiceException
59 List<String> selectedRepos = getObservableRepos();
60 if ( CollectionUtils.isEmpty( selectedRepos ) )
63 return new BrowseResult();
66 Set<String> namespaces = new LinkedHashSet<String>();
68 // TODO: this logic should be optional, particularly remembering we want to keep this code simple
69 // it is located here to avoid the content repository implementation needing to do too much for what
70 // is essentially presentation code
71 Set<String> namespacesToCollapse;
72 RepositorySession repositorySession = repositorySessionFactory.createSession();
75 MetadataResolver metadataResolver = repositorySession.getResolver();
76 namespacesToCollapse = new LinkedHashSet<String>();
77 for ( String repoId : selectedRepos )
79 namespacesToCollapse.addAll( metadataResolver.resolveRootNamespaces( repositorySession, repoId ) );
81 for ( String n : namespacesToCollapse )
83 // TODO: check performance of this
84 namespaces.add( collapseNamespaces( repositorySession, metadataResolver, selectedRepos, n ) );
87 catch ( MetadataResolutionException e )
89 throw new ArchivaRestServiceException( e.getMessage(),
90 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
94 repositorySession.close();
97 List<BrowseResultEntry> browseGroupResultEntries = new ArrayList<BrowseResultEntry>( namespaces.size() );
98 for ( String namespace : namespaces )
100 browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
103 Collections.sort( browseGroupResultEntries );
104 return new BrowseResult( browseGroupResultEntries );
107 public BrowseResult browseGroupId( String groupId )
108 throws ArchivaRestServiceException
111 List<String> selectedRepos = getObservableRepos();
112 if ( CollectionUtils.isEmpty( selectedRepos ) )
115 return new BrowseResult();
118 Set<String> projects = new LinkedHashSet<String>();
120 RepositorySession repositorySession = repositorySessionFactory.createSession();
121 Set<String> namespaces;
124 MetadataResolver metadataResolver = repositorySession.getResolver();
126 Set<String> namespacesToCollapse = new LinkedHashSet<String>();
127 for ( String repoId : selectedRepos )
129 namespacesToCollapse.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, groupId ) );
131 projects.addAll( metadataResolver.resolveProjects( repositorySession, repoId, groupId ) );
134 // TODO: this logic should be optional, particularly remembering we want to keep this code simple
135 // it is located here to avoid the content repository implementation needing to do too much for what
136 // is essentially presentation code
137 namespaces = new LinkedHashSet<String>();
138 for ( String n : namespacesToCollapse )
140 // TODO: check performance of this
142 collapseNamespaces( repositorySession, metadataResolver, selectedRepos, groupId + "." + n ) );
145 catch ( MetadataResolutionException e )
147 throw new ArchivaRestServiceException( e.getMessage(),
148 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
152 repositorySession.close();
154 List<BrowseResultEntry> browseGroupResultEntries =
155 new ArrayList<BrowseResultEntry>( namespaces.size() + projects.size() );
156 for ( String namespace : namespaces )
158 browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
160 for ( String project : projects )
162 browseGroupResultEntries.add( new BrowseResultEntry( groupId + '.' + project, true ) );
164 Collections.sort( browseGroupResultEntries );
165 return new BrowseResult( browseGroupResultEntries );
169 public VersionsList getVersionsList( String groupId, String artifactId )
170 throws ArchivaRestServiceException
172 List<String> selectedRepos = getObservableRepos();
173 if ( CollectionUtils.isEmpty( selectedRepos ) )
176 return new VersionsList();
181 return new VersionsList( new ArrayList<String>( getVersions( selectedRepos, groupId, artifactId ) ) );
183 catch ( MetadataResolutionException e )
185 throw new ArchivaRestServiceException( e.getMessage(),
186 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
191 private Collection<String> getVersions( List<String> selectedRepos, String groupId, String artifactId )
192 throws MetadataResolutionException
195 RepositorySession repositorySession = repositorySessionFactory.createSession();
198 MetadataResolver metadataResolver = repositorySession.getResolver();
200 Set<String> versions = new LinkedHashSet<String>();
202 for ( String repoId : selectedRepos )
205 metadataResolver.resolveProjectVersions( repositorySession, repoId, groupId, artifactId ) );
208 List<String> sortedVersions = new ArrayList<String>( versions );
210 Collections.sort( sortedVersions, VersionComparator.getInstance() );
212 return sortedVersions;
216 repositorySession.close();
220 public ProjectVersionMetadata getProjectVersionMetadata( String groupId, String artifactId )
221 throws ArchivaRestServiceException
224 List<String> selectedRepos = getObservableRepos();
226 if ( CollectionUtils.isEmpty( selectedRepos ) )
232 RepositorySession repositorySession = null;
236 Collection<String> projectVersions = getVersions( selectedRepos, groupId, artifactId );
238 repositorySession = repositorySessionFactory.createSession();
240 MetadataResolver metadataResolver = repositorySession.getResolver();
242 ProjectVersionMetadata sharedModel = new ProjectVersionMetadata();
244 MavenProjectFacet mavenFacet = new MavenProjectFacet();
245 mavenFacet.setGroupId( groupId );
246 mavenFacet.setArtifactId( artifactId );
247 sharedModel.addFacet( mavenFacet );
249 boolean isFirstVersion = true;
251 for ( String version : projectVersions )
253 ProjectVersionMetadata versionMetadata = null;
254 for ( String repoId : selectedRepos )
256 if ( versionMetadata == null )
261 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
264 catch ( MetadataResolutionException e )
266 log.error( "Skipping invalid metadata while compiling shared model for " + groupId + ":"
267 + artifactId + " in repo " + repoId + ": " + e.getMessage() );
272 if ( versionMetadata == null )
277 if ( isFirstVersion )
279 sharedModel = versionMetadata;
280 sharedModel.setId( null );
284 MavenProjectFacet versionMetadataMavenFacet =
285 (MavenProjectFacet) versionMetadata.getFacet( MavenProjectFacet.FACET_ID );
286 if ( versionMetadataMavenFacet != null )
288 if ( mavenFacet.getPackaging() != null && !StringUtils.equalsIgnoreCase(
289 mavenFacet.getPackaging(), versionMetadataMavenFacet.getPackaging() ) )
291 mavenFacet.setPackaging( null );
295 if ( sharedModel.getName() != null && !StringUtils.equalsIgnoreCase( sharedModel.getName(),
296 versionMetadata.getName() ) )
298 sharedModel.setName( "" );
301 if ( sharedModel.getDescription() != null && !StringUtils.equalsIgnoreCase(
302 sharedModel.getDescription(), versionMetadata.getDescription() ) )
304 sharedModel.setDescription( StringUtils.isNotEmpty( versionMetadata.getDescription() )
305 ? versionMetadata.getDescription()
309 if ( sharedModel.getIssueManagement() != null && versionMetadata.getIssueManagement() != null
310 && !StringUtils.equalsIgnoreCase( sharedModel.getIssueManagement().getUrl(),
311 versionMetadata.getIssueManagement().getUrl() ) )
313 sharedModel.setIssueManagement( null );
316 if ( sharedModel.getCiManagement() != null && versionMetadata.getCiManagement() != null
317 && !StringUtils.equalsIgnoreCase( sharedModel.getCiManagement().getUrl(),
318 versionMetadata.getCiManagement().getUrl() ) )
320 sharedModel.setCiManagement( null );
323 if ( sharedModel.getOrganization() != null && versionMetadata.getOrganization() != null
324 && !StringUtils.equalsIgnoreCase( sharedModel.getOrganization().getName(),
325 versionMetadata.getOrganization().getName() ) )
327 sharedModel.setOrganization( null );
330 if ( sharedModel.getUrl() != null && !StringUtils.equalsIgnoreCase( sharedModel.getUrl(),
331 versionMetadata.getUrl() ) )
333 sharedModel.setUrl( null );
337 isFirstVersion = false;
341 catch ( MetadataResolutionException e )
343 throw new ArchivaRestServiceException( e.getMessage(),
344 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
348 if ( repositorySession != null )
350 repositorySession.close();
355 public List<ManagedRepository> getUserRepositories()
356 throws ArchivaRestServiceException
360 return userRepositories.getAccessibleRepositories( getPrincipal() );
362 catch ( ArchivaSecurityException e )
364 throw new ArchivaRestServiceException( "repositories.read.observable.error",
365 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
369 //---------------------------
371 //---------------------------
373 private List<String> getSortedList( Set<String> set )
375 List<String> list = new ArrayList<String>( set );
376 Collections.sort( list );
380 private String collapseNamespaces( RepositorySession repositorySession, MetadataResolver metadataResolver,
381 Collection<String> repoIds, String n )
382 throws MetadataResolutionException
384 Set<String> subNamespaces = new LinkedHashSet<String>();
385 for ( String repoId : repoIds )
387 subNamespaces.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, n ) );
389 if ( subNamespaces.size() != 1 )
391 log.debug( "{} is not collapsible as it has sub-namespaces: {}", n, subNamespaces );
396 for ( String repoId : repoIds )
398 Collection<String> projects = metadataResolver.resolveProjects( repositorySession, repoId, n );
399 if ( projects != null && !projects.isEmpty() )
401 log.debug( "{} is not collapsible as it has projects", n );
405 return collapseNamespaces( repositorySession, metadataResolver, repoIds,
406 n + "." + subNamespaces.iterator().next() );