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.rest.api.model.Artifact;
35 import org.apache.archiva.rest.api.model.ArtifactContentEntry;
36 import org.apache.archiva.rest.api.model.BrowseResult;
37 import org.apache.archiva.rest.api.model.BrowseResultEntry;
38 import org.apache.archiva.rest.api.model.Entry;
39 import org.apache.archiva.rest.api.model.TreeEntry;
40 import org.apache.archiva.rest.api.model.VersionsList;
41 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
42 import org.apache.archiva.rest.api.services.BrowseService;
43 import org.apache.archiva.rest.services.utils.TreeDependencyNodeVisitor;
44 import org.apache.archiva.security.ArchivaSecurityException;
45 import org.apache.commons.collections.CollectionUtils;
46 import org.apache.commons.lang.StringUtils;
47 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
48 import org.springframework.stereotype.Service;
50 import javax.inject.Inject;
51 import javax.ws.rs.core.Response;
52 import java.util.ArrayList;
53 import java.util.Collection;
54 import java.util.Collections;
55 import java.util.HashMap;
56 import java.util.LinkedHashSet;
57 import java.util.List;
62 * @author Olivier Lamy
65 @Service( "browseService#rest" )
66 public class DefaultBrowseService
67 extends AbstractRestService
68 implements BrowseService
72 private DependencyTreeBuilder dependencyTreeBuilder;
74 public BrowseResult getRootGroups( String repositoryId )
75 throws ArchivaRestServiceException
77 List<String> selectedRepos = getObservableRepos();
78 if ( CollectionUtils.isEmpty( selectedRepos ) )
81 return new BrowseResult();
84 if ( StringUtils.isNotEmpty( repositoryId ) )
86 // check user has karma on the repository
87 if ( !selectedRepos.contains( repositoryId ) )
89 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
90 Response.Status.FORBIDDEN.getStatusCode() );
92 selectedRepos = Collections.singletonList( repositoryId );
95 Set<String> namespaces = new LinkedHashSet<String>();
97 // TODO: this logic should be optional, particularly remembering we want to keep this code simple
98 // it is located here to avoid the content repository implementation needing to do too much for what
99 // is essentially presentation code
100 Set<String> namespacesToCollapse;
101 RepositorySession repositorySession = repositorySessionFactory.createSession();
104 MetadataResolver metadataResolver = repositorySession.getResolver();
105 namespacesToCollapse = new LinkedHashSet<String>();
107 for ( String repoId : selectedRepos )
109 namespacesToCollapse.addAll( metadataResolver.resolveRootNamespaces( repositorySession, repoId ) );
111 for ( String n : namespacesToCollapse )
113 // TODO: check performance of this
114 namespaces.add( collapseNamespaces( repositorySession, metadataResolver, selectedRepos, n ) );
117 catch ( MetadataResolutionException e )
119 throw new ArchivaRestServiceException( e.getMessage(),
120 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
124 repositorySession.close();
127 List<BrowseResultEntry> browseGroupResultEntries = new ArrayList<BrowseResultEntry>( namespaces.size() );
128 for ( String namespace : namespaces )
130 browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
133 Collections.sort( browseGroupResultEntries );
134 return new BrowseResult( browseGroupResultEntries );
137 public BrowseResult browseGroupId( String groupId, String repositoryId )
138 throws ArchivaRestServiceException
141 List<String> selectedRepos = getObservableRepos();
142 if ( CollectionUtils.isEmpty( selectedRepos ) )
145 return new BrowseResult();
148 if ( StringUtils.isNotEmpty( repositoryId ) )
150 // check user has karma on the repository
151 if ( !selectedRepos.contains( repositoryId ) )
153 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
154 Response.Status.FORBIDDEN.getStatusCode() );
156 selectedRepos = Collections.singletonList( repositoryId );
159 Set<String> projects = new LinkedHashSet<String>();
161 RepositorySession repositorySession = repositorySessionFactory.createSession();
162 Set<String> namespaces;
165 MetadataResolver metadataResolver = repositorySession.getResolver();
167 Set<String> namespacesToCollapse = new LinkedHashSet<String>();
168 for ( String repoId : selectedRepos )
170 namespacesToCollapse.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, groupId ) );
172 projects.addAll( metadataResolver.resolveProjects( repositorySession, repoId, groupId ) );
175 // TODO: this logic should be optional, particularly remembering we want to keep this code simple
176 // it is located here to avoid the content repository implementation needing to do too much for what
177 // is essentially presentation code
178 namespaces = new LinkedHashSet<String>();
179 for ( String n : namespacesToCollapse )
181 // TODO: check performance of this
183 collapseNamespaces( repositorySession, metadataResolver, selectedRepos, groupId + "." + n ) );
186 catch ( MetadataResolutionException e )
188 throw new ArchivaRestServiceException( e.getMessage(),
189 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
193 repositorySession.close();
195 List<BrowseResultEntry> browseGroupResultEntries =
196 new ArrayList<BrowseResultEntry>( namespaces.size() + projects.size() );
197 for ( String namespace : namespaces )
199 browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
201 for ( String project : projects )
203 browseGroupResultEntries.add( new BrowseResultEntry( groupId + '.' + project, true ) );
205 Collections.sort( browseGroupResultEntries );
206 return new BrowseResult( browseGroupResultEntries );
210 public VersionsList getVersionsList( String groupId, String artifactId, String repositoryId )
211 throws ArchivaRestServiceException
213 List<String> selectedRepos = getObservableRepos();
214 if ( CollectionUtils.isEmpty( selectedRepos ) )
217 return new VersionsList();
220 if ( StringUtils.isNotEmpty( repositoryId ) )
222 // check user has karma on the repository
223 if ( !selectedRepos.contains( repositoryId ) )
225 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
226 Response.Status.FORBIDDEN.getStatusCode() );
228 selectedRepos = Collections.singletonList( repositoryId );
233 return new VersionsList( new ArrayList<String>( getVersions( selectedRepos, groupId, artifactId ) ) );
235 catch ( MetadataResolutionException e )
237 throw new ArchivaRestServiceException( e.getMessage(),
238 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
243 private Collection<String> getVersions( List<String> selectedRepos, String groupId, String artifactId )
244 throws MetadataResolutionException
247 RepositorySession repositorySession = repositorySessionFactory.createSession();
250 MetadataResolver metadataResolver = repositorySession.getResolver();
252 Set<String> versions = new LinkedHashSet<String>();
254 for ( String repoId : selectedRepos )
257 metadataResolver.resolveProjectVersions( repositorySession, repoId, groupId, artifactId ) );
260 List<String> sortedVersions = new ArrayList<String>( versions );
262 Collections.sort( sortedVersions, VersionComparator.getInstance() );
264 return sortedVersions;
268 repositorySession.close();
272 public ProjectVersionMetadata getProjectMetadata( String groupId, String artifactId, String version,
273 String repositoryId )
274 throws ArchivaRestServiceException
276 List<String> selectedRepos = getObservableRepos();
278 if ( CollectionUtils.isEmpty( selectedRepos ) )
284 if ( StringUtils.isNotEmpty( repositoryId ) )
286 // check user has karma on the repository
287 if ( !selectedRepos.contains( repositoryId ) )
289 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
290 Response.Status.FORBIDDEN.getStatusCode() );
292 selectedRepos = Collections.singletonList( repositoryId );
295 RepositorySession repositorySession = null;
298 repositorySession = repositorySessionFactory.createSession();
300 MetadataResolver metadataResolver = repositorySession.getResolver();
302 ProjectVersionMetadata versionMetadata = null;
303 for ( String repoId : selectedRepos )
305 if ( versionMetadata == null || versionMetadata.isIncomplete() )
310 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
313 catch ( MetadataResolutionException e )
316 "Skipping invalid metadata while compiling shared model for " + groupId + ":" + artifactId
317 + " in repo " + repoId + ": " + e.getMessage() );
322 return versionMetadata;
326 if ( repositorySession != null )
328 repositorySession.close();
334 public ProjectVersionMetadata getProjectVersionMetadata( String groupId, String artifactId, String repositoryId )
335 throws ArchivaRestServiceException
338 List<String> selectedRepos = getObservableRepos();
340 if ( CollectionUtils.isEmpty( selectedRepos ) )
346 if ( StringUtils.isNotEmpty( repositoryId ) )
348 // check user has karma on the repository
349 if ( !selectedRepos.contains( repositoryId ) )
351 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
352 Response.Status.FORBIDDEN.getStatusCode() );
354 selectedRepos = Collections.singletonList( repositoryId );
357 RepositorySession repositorySession = null;
361 Collection<String> projectVersions = getVersions( selectedRepos, groupId, artifactId );
363 repositorySession = repositorySessionFactory.createSession();
365 MetadataResolver metadataResolver = repositorySession.getResolver();
367 ProjectVersionMetadata sharedModel = new ProjectVersionMetadata();
369 MavenProjectFacet mavenFacet = new MavenProjectFacet();
370 mavenFacet.setGroupId( groupId );
371 mavenFacet.setArtifactId( artifactId );
372 sharedModel.addFacet( mavenFacet );
374 boolean isFirstVersion = true;
376 for ( String version : projectVersions )
378 ProjectVersionMetadata versionMetadata = null;
379 for ( String repoId : selectedRepos )
381 if ( versionMetadata == null || versionMetadata.isIncomplete() )
386 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
389 catch ( MetadataResolutionException e )
391 log.error( "Skipping invalid metadata while compiling shared model for " + groupId + ":"
392 + artifactId + " in repo " + repoId + ": " + e.getMessage() );
397 if ( versionMetadata == null )
402 if ( isFirstVersion )
404 sharedModel = versionMetadata;
405 sharedModel.setId( null );
409 MavenProjectFacet versionMetadataMavenFacet =
410 (MavenProjectFacet) versionMetadata.getFacet( MavenProjectFacet.FACET_ID );
411 if ( versionMetadataMavenFacet != null )
413 if ( mavenFacet.getPackaging() != null && !StringUtils.equalsIgnoreCase(
414 mavenFacet.getPackaging(), versionMetadataMavenFacet.getPackaging() ) )
416 mavenFacet.setPackaging( null );
420 if ( StringUtils.isEmpty( sharedModel.getName() ) && !StringUtils.isEmpty(
421 versionMetadata.getName() ) )
423 sharedModel.setName( versionMetadata.getName() );
426 if ( sharedModel.getDescription() != null && !StringUtils.equalsIgnoreCase(
427 sharedModel.getDescription(), versionMetadata.getDescription() ) )
429 sharedModel.setDescription( StringUtils.isNotEmpty( versionMetadata.getDescription() )
430 ? versionMetadata.getDescription()
434 if ( sharedModel.getIssueManagement() != null && versionMetadata.getIssueManagement() != null
435 && !StringUtils.equalsIgnoreCase( sharedModel.getIssueManagement().getUrl(),
436 versionMetadata.getIssueManagement().getUrl() ) )
438 sharedModel.setIssueManagement( versionMetadata.getIssueManagement() );
441 if ( sharedModel.getCiManagement() != null && versionMetadata.getCiManagement() != null
442 && !StringUtils.equalsIgnoreCase( sharedModel.getCiManagement().getUrl(),
443 versionMetadata.getCiManagement().getUrl() ) )
445 sharedModel.setCiManagement( versionMetadata.getCiManagement() );
448 if ( sharedModel.getOrganization() != null && versionMetadata.getOrganization() != null
449 && !StringUtils.equalsIgnoreCase( sharedModel.getOrganization().getName(),
450 versionMetadata.getOrganization().getName() ) )
452 sharedModel.setOrganization( versionMetadata.getOrganization() );
455 if ( sharedModel.getUrl() != null && !StringUtils.equalsIgnoreCase( sharedModel.getUrl(),
456 versionMetadata.getUrl() ) )
458 sharedModel.setUrl( versionMetadata.getUrl() );
462 isFirstVersion = false;
466 catch ( MetadataResolutionException e )
468 throw new ArchivaRestServiceException( e.getMessage(),
469 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
473 if ( repositorySession != null )
475 repositorySession.close();
480 public List<TreeEntry> getTreeEntries( String groupId, String artifactId, String version, String repositoryId )
481 throws ArchivaRestServiceException
483 List<String> selectedRepos = getObservableRepos();
485 if ( CollectionUtils.isEmpty( selectedRepos ) )
491 if ( StringUtils.isNotEmpty( repositoryId ) )
493 // check user has karma on the repository
494 if ( !selectedRepos.contains( repositoryId ) )
496 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
497 Response.Status.FORBIDDEN.getStatusCode() );
499 selectedRepos = Collections.singletonList( repositoryId );
502 List<TreeEntry> treeEntries = new ArrayList<TreeEntry>();
503 TreeDependencyNodeVisitor treeDependencyNodeVisitor = new TreeDependencyNodeVisitor( treeEntries );
506 dependencyTreeBuilder.buildDependencyTree( selectedRepos, groupId, artifactId, version,
507 treeDependencyNodeVisitor );
509 catch ( DependencyTreeBuilderException e )
511 throw new ArchivaRestServiceException( e.getMessage(),
512 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
517 public List<ManagedRepository> getUserRepositories()
518 throws ArchivaRestServiceException
522 return userRepositories.getAccessibleRepositories( getPrincipal() );
524 catch ( ArchivaSecurityException e )
526 throw new ArchivaRestServiceException( "repositories.read.observable.error",
527 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
531 public List<Artifact> getDependees( String groupId, String artifactId, String version, String repositoryId )
532 throws ArchivaRestServiceException
534 List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
535 // TODO: what if we get duplicates across repositories?
536 RepositorySession repositorySession = repositorySessionFactory.createSession();
539 MetadataResolver metadataResolver = repositorySession.getResolver();
540 for ( String repoId : getObservableRepos() )
542 // TODO: what about if we want to see this irrespective of version?
544 metadataResolver.resolveProjectReferences( repositorySession, repoId, groupId, artifactId,
548 catch ( MetadataResolutionException e )
550 throw new ArchivaRestServiceException( e.getMessage(),
551 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
555 repositorySession.close();
558 List<Artifact> artifacts = new ArrayList<Artifact>( references.size() );
560 for ( ProjectVersionReference projectVersionReference : references )
562 artifacts.add( new Artifact( projectVersionReference.getNamespace(), projectVersionReference.getProjectId(),
563 projectVersionReference.getProjectVersion() ) );
568 public List<Entry> getMetadatas( String groupId, String artifactId, String version, String repositoryId )
569 throws ArchivaRestServiceException
571 ProjectVersionMetadata projectVersionMetadata =
572 getProjectMetadata( groupId, artifactId, version, repositoryId );
573 if ( projectVersionMetadata == null )
575 return Collections.emptyList();
577 MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
579 if ( metadataFacet == null )
581 return Collections.emptyList();
583 Map<String, String> map = metadataFacet.toProperties();
584 List<Entry> entries = new ArrayList<Entry>( map.size() );
586 for ( Map.Entry<String, String> entry : map.entrySet() )
588 entries.add( new Entry( entry.getKey(), entry.getValue() ) );
594 public Boolean addMetadata( String groupId, String artifactId, String version, String key, String value,
595 String repositoryId )
596 throws ArchivaRestServiceException
598 ProjectVersionMetadata projectVersionMetadata =
599 getProjectMetadata( groupId, artifactId, version, repositoryId );
601 if ( projectVersionMetadata == null )
603 return Boolean.FALSE;
606 Map<String, String> properties = new HashMap<String, String>();
608 MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
610 if ( metadataFacet != null && metadataFacet.toProperties() != null )
612 properties.putAll( metadataFacet.toProperties() );
616 metadataFacet = new GenericMetadataFacet();
619 properties.put( key, value );
621 metadataFacet.fromProperties( properties );
623 projectVersionMetadata.addFacet( metadataFacet );
625 RepositorySession repositorySession = repositorySessionFactory.createSession();
629 MetadataRepository metadataRepository = repositorySession.getRepository();
631 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
633 repositorySession.save();
635 catch ( MetadataRepositoryException e )
637 log.error( e.getMessage(), e );
638 throw new ArchivaRestServiceException( e.getMessage(),
639 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
643 repositorySession.close();
648 public Boolean deleteMetadata( String groupId, String artifactId, String version, String key, String repositoryId )
649 throws ArchivaRestServiceException
651 ProjectVersionMetadata projectVersionMetadata =
652 getProjectMetadata( groupId, artifactId, version, repositoryId );
654 if ( projectVersionMetadata == null )
656 return Boolean.FALSE;
659 GenericMetadataFacet metadataFacet =
660 (GenericMetadataFacet) projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
662 if ( metadataFacet != null && metadataFacet.toProperties() != null )
664 Map<String, String> properties = metadataFacet.toProperties();
665 properties.remove( key );
666 metadataFacet.setAdditionalProperties( properties );
673 RepositorySession repositorySession = repositorySessionFactory.createSession();
677 MetadataRepository metadataRepository = repositorySession.getRepository();
679 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
681 repositorySession.save();
683 catch ( MetadataRepositoryException e )
685 log.error( e.getMessage(), e );
686 throw new ArchivaRestServiceException( e.getMessage(),
687 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode() );
691 repositorySession.close();
696 public List<ArtifactContentEntry> getArtifactContentEntries( String groupId, String artifactId, String version,
697 String path, String repositoryId )
698 throws ArchivaRestServiceException
703 //---------------------------
705 //---------------------------
707 private List<String> getSortedList( Set<String> set )
709 List<String> list = new ArrayList<String>( set );
710 Collections.sort( list );
714 private String collapseNamespaces( RepositorySession repositorySession, MetadataResolver metadataResolver,
715 Collection<String> repoIds, String n )
716 throws MetadataResolutionException
718 Set<String> subNamespaces = new LinkedHashSet<String>();
719 for ( String repoId : repoIds )
721 subNamespaces.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, n ) );
723 if ( subNamespaces.size() != 1 )
725 log.debug( "{} is not collapsible as it has sub-namespaces: {}", n, subNamespaces );
730 for ( String repoId : repoIds )
732 Collection<String> projects = metadataResolver.resolveProjects( repositorySession, repoId, n );
733 if ( projects != null && !projects.isEmpty() )
735 log.debug( "{} is not collapsible as it has projects", n );
739 return collapseNamespaces( repositorySession, metadataResolver, repoIds,
740 n + "." + subNamespaces.iterator().next() );