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.common.utils.VersionUtil;
24 import org.apache.archiva.dependency.tree.maven2.DependencyTreeBuilder;
25 import org.apache.archiva.maven2.metadata.MavenMetadataReader;
26 import org.apache.archiva.maven2.model.Artifact;
27 import org.apache.archiva.maven2.model.TreeEntry;
28 import org.apache.archiva.metadata.generic.GenericMetadataFacet;
29 import org.apache.archiva.metadata.model.ArtifactMetadata;
30 import org.apache.archiva.metadata.model.MetadataFacet;
31 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
32 import org.apache.archiva.metadata.model.ProjectVersionReference;
33 import org.apache.archiva.metadata.repository.*;
34 import org.apache.archiva.metadata.repository.storage.maven2.ArtifactMetadataVersionComparator;
35 import org.apache.archiva.metadata.repository.storage.maven2.MavenProjectFacet;
36 import org.apache.archiva.model.ArchivaArtifact;
37 import org.apache.archiva.model.ArchivaRepositoryMetadata;
38 import org.apache.archiva.proxy.ProxyRegistry;
39 import org.apache.archiva.proxy.model.RepositoryProxyHandler;
40 import org.apache.archiva.redback.components.cache.Cache;
41 import org.apache.archiva.repository.ManagedRepositoryContent;
42 import org.apache.archiva.repository.ReleaseScheme;
43 import org.apache.archiva.repository.RepositoryException;
44 import org.apache.archiva.repository.RepositoryNotFoundException;
45 import org.apache.archiva.repository.metadata.MetadataTools;
46 import org.apache.archiva.rest.api.model.*;
47 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
48 import org.apache.archiva.rest.api.services.BrowseService;
49 import org.apache.archiva.rest.services.utils.ArtifactContentEntryComparator;
50 import org.apache.archiva.security.ArchivaSecurityException;
51 import org.apache.archiva.xml.XMLException;
52 import org.apache.commons.collections4.CollectionUtils;
53 import org.apache.commons.io.IOUtils;
54 import org.apache.commons.lang.StringUtils;
55 import org.springframework.stereotype.Service;
57 import javax.inject.Inject;
58 import javax.inject.Named;
59 import javax.ws.rs.core.Response;
60 import java.io.IOException;
61 import java.io.InputStream;
62 import java.nio.charset.Charset;
63 import java.nio.file.Files;
64 import java.nio.file.Path;
66 import java.util.jar.JarEntry;
67 import java.util.jar.JarFile;
68 import java.util.zip.ZipEntry;
71 * @author Olivier Lamy
74 @Service( "browseService#rest" )
75 public class DefaultBrowseService
76 extends AbstractRestService
77 implements BrowseService
80 private final Charset ARTIFACT_CONTENT_ENCODING=Charset.forName( "UTF-8" );
83 private DependencyTreeBuilder dependencyTreeBuilder;
86 ProxyRegistry proxyRegistry;
89 @Named( value = "browse#versionMetadata" )
90 private Cache<String, ProjectVersionMetadata> versionMetadataCache;
92 private ManagedRepositoryContent getManagedRepositoryContent(String id) throws RepositoryException
94 org.apache.archiva.repository.ManagedRepository repo = repositoryRegistry.getManagedRepository( id );
96 throw new RepositoryException( "Could not find repository "+id );
98 return repo.getContent();
102 public BrowseResult getRootGroups( String repositoryId )
103 throws ArchivaRestServiceException
105 List<String> selectedRepos = getSelectedRepos( repositoryId );
107 Set<String> namespaces = new LinkedHashSet<String>();
109 // TODO: this logic should be optional, particularly remembering we want to keep this code simple
110 // it is located here to avoid the content repository implementation needing to do too much for what
111 // is essentially presentation code
112 Set<String> namespacesToCollapse = new LinkedHashSet<String>();
113 RepositorySession repositorySession = repositorySessionFactory.createSession();
116 MetadataResolver metadataResolver = repositorySession.getResolver();
118 for ( String repoId : selectedRepos )
120 namespacesToCollapse.addAll( metadataResolver.resolveRootNamespaces( repositorySession, repoId ) );
122 for ( String n : namespacesToCollapse )
124 // TODO: check performance of this
125 namespaces.add( collapseNamespaces( repositorySession, metadataResolver, selectedRepos, n ) );
128 catch ( MetadataResolutionException e )
130 throw new ArchivaRestServiceException( e.getMessage(),
131 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
135 repositorySession.close();
138 List<BrowseResultEntry> browseGroupResultEntries = new ArrayList<>( namespaces.size() );
139 for ( String namespace : namespaces )
141 browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
144 Collections.sort( browseGroupResultEntries );
145 return new BrowseResult( browseGroupResultEntries );
149 public BrowseResult browseGroupId( String groupId, String repositoryId )
150 throws ArchivaRestServiceException
152 List<String> selectedRepos = getSelectedRepos( repositoryId );
154 Set<String> projects = new LinkedHashSet<>();
156 RepositorySession repositorySession = repositorySessionFactory.createSession();
157 Set<String> namespaces;
160 MetadataResolver metadataResolver = repositorySession.getResolver();
162 Set<String> namespacesToCollapse = new LinkedHashSet<>();
163 for ( String repoId : selectedRepos )
165 namespacesToCollapse.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, groupId ) );
167 projects.addAll( metadataResolver.resolveProjects( repositorySession, repoId, groupId ) );
170 // TODO: this logic should be optional, particularly remembering we want to keep this code simple
171 // it is located here to avoid the content repository implementation needing to do too much for what
172 // is essentially presentation code
173 namespaces = new LinkedHashSet<>();
174 for ( String n : namespacesToCollapse )
176 // TODO: check performance of this
178 collapseNamespaces( repositorySession, metadataResolver, selectedRepos, groupId + "." + n ) );
181 catch ( MetadataResolutionException e )
183 throw new ArchivaRestServiceException( e.getMessage(),
184 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
188 repositorySession.close();
190 List<BrowseResultEntry> browseGroupResultEntries = new ArrayList<>( namespaces.size() + projects.size() );
191 for ( String namespace : namespaces )
193 browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ).groupId( namespace ) );
195 for ( String project : projects )
197 browseGroupResultEntries.add(
198 new BrowseResultEntry( groupId + '.' + project, true ).groupId( groupId ).artifactId( project ) );
200 Collections.sort( browseGroupResultEntries );
201 return new BrowseResult( browseGroupResultEntries );
206 public VersionsList getVersionsList( String groupId, String artifactId, String repositoryId )
207 throws ArchivaRestServiceException
209 List<String> selectedRepos = getSelectedRepos( repositoryId );
213 Collection<String> versions = getVersions( selectedRepos, groupId, artifactId );
214 return new VersionsList( new ArrayList<>( versions ) );
216 catch ( MetadataResolutionException e )
218 throw new ArchivaRestServiceException( e.getMessage(),
219 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
224 private Collection<String> getVersions( List<String> selectedRepos, String groupId, String artifactId )
225 throws MetadataResolutionException
228 RepositorySession repositorySession = repositorySessionFactory.createSession();
231 MetadataResolver metadataResolver = repositorySession.getResolver();
233 Set<String> versions = new LinkedHashSet<String>();
235 for ( String repoId : selectedRepos )
237 Collection<String> projectVersions =
238 metadataResolver.resolveProjectVersions( repositorySession, repoId, groupId, artifactId );
239 versions.addAll( projectVersions );
242 List<String> sortedVersions = new ArrayList<>( versions );
244 Collections.sort( sortedVersions, VersionComparator.getInstance() );
246 return sortedVersions;
250 repositorySession.close();
255 public ProjectVersionMetadata getProjectMetadata( String groupId, String artifactId, String version,
256 String repositoryId )
257 throws ArchivaRestServiceException
259 List<String> selectedRepos = getSelectedRepos( repositoryId );
261 RepositorySession repositorySession = null;
264 repositorySession = repositorySessionFactory.createSession();
266 MetadataResolver metadataResolver = repositorySession.getResolver();
268 ProjectVersionMetadata versionMetadata = null;
269 for ( String repoId : selectedRepos )
271 if ( versionMetadata == null || versionMetadata.isIncomplete() )
275 ProjectVersionMetadata versionMetadataTmp =
276 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
279 if ( versionMetadata == null && versionMetadataTmp != null )
281 versionMetadata = versionMetadataTmp;
286 catch ( MetadataResolutionException e )
288 log.warn( "Skipping invalid metadata while compiling shared model for {}:{} in repo {}: {}",
289 groupId, artifactId, repoId, e.getMessage() );
294 return versionMetadata;
298 if ( repositorySession != null )
300 repositorySession.close();
307 public ProjectVersionMetadata getProjectVersionMetadata( String groupId, String artifactId, String repositoryId )
308 throws ArchivaRestServiceException
311 List<String> selectedRepos = getSelectedRepos( repositoryId );
313 RepositorySession repositorySession = null;
317 Collection<String> projectVersions = getVersions( selectedRepos, groupId, artifactId );
319 repositorySession = repositorySessionFactory.createSession();
321 MetadataResolver metadataResolver = repositorySession.getResolver();
323 ProjectVersionMetadata sharedModel = new ProjectVersionMetadata();
325 MavenProjectFacet mavenFacet = new MavenProjectFacet();
326 mavenFacet.setGroupId( groupId );
327 mavenFacet.setArtifactId( artifactId );
328 sharedModel.addFacet( mavenFacet );
330 boolean isFirstVersion = true;
332 for ( String version : projectVersions )
334 ProjectVersionMetadata versionMetadata = null;
335 for ( String repoId : selectedRepos )
337 if ( versionMetadata == null || versionMetadata.isIncomplete() )
341 ProjectVersionMetadata projectVersionMetadataResolved = null;
342 boolean useCache = !StringUtils.endsWith( version, VersionUtil.SNAPSHOT );
343 String cacheKey = null;
344 boolean cacheToUpdate = false;
345 // FIXME a bit maven centric!!!
346 // not a snapshot so get it from cache
349 cacheKey = repoId + groupId + artifactId + version;
350 projectVersionMetadataResolved = versionMetadataCache.get( cacheKey );
352 if ( useCache && projectVersionMetadataResolved != null )
354 versionMetadata = projectVersionMetadataResolved;
358 projectVersionMetadataResolved =
359 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId,
360 artifactId, version );
361 versionMetadata = projectVersionMetadataResolved;
362 cacheToUpdate = true;
365 if ( useCache && cacheToUpdate )
367 versionMetadataCache.put( cacheKey, projectVersionMetadataResolved );
371 catch ( MetadataResolutionException e )
373 log.error( "Skipping invalid metadata while compiling shared model for " + groupId + ":"
374 + artifactId + " in repo " + repoId + ": " + e.getMessage() );
379 if ( versionMetadata == null )
384 if ( isFirstVersion )
386 sharedModel = versionMetadata;
387 sharedModel.setId( null );
391 MavenProjectFacet versionMetadataMavenFacet =
392 (MavenProjectFacet) versionMetadata.getFacet( MavenProjectFacet.FACET_ID );
393 if ( versionMetadataMavenFacet != null )
395 if ( mavenFacet.getPackaging() != null //
396 && !StringUtils.equalsIgnoreCase( mavenFacet.getPackaging(),
397 versionMetadataMavenFacet.getPackaging() ) )
399 mavenFacet.setPackaging( null );
403 if ( StringUtils.isEmpty( sharedModel.getName() ) //
404 && !StringUtils.isEmpty( versionMetadata.getName() ) )
406 sharedModel.setName( versionMetadata.getName() );
409 if ( sharedModel.getDescription() != null //
410 && !StringUtils.equalsIgnoreCase( sharedModel.getDescription(),
411 versionMetadata.getDescription() ) )
413 sharedModel.setDescription( StringUtils.isNotEmpty( versionMetadata.getDescription() )
414 ? versionMetadata.getDescription()
418 if ( sharedModel.getIssueManagement() != null //
419 && versionMetadata.getIssueManagement() != null //
420 && !StringUtils.equalsIgnoreCase( sharedModel.getIssueManagement().getUrl(),
421 versionMetadata.getIssueManagement().getUrl() ) )
423 sharedModel.setIssueManagement( versionMetadata.getIssueManagement() );
426 if ( sharedModel.getCiManagement() != null //
427 && versionMetadata.getCiManagement() != null //
428 && !StringUtils.equalsIgnoreCase( sharedModel.getCiManagement().getUrl(),
429 versionMetadata.getCiManagement().getUrl() ) )
431 sharedModel.setCiManagement( versionMetadata.getCiManagement() );
434 if ( sharedModel.getOrganization() != null //
435 && versionMetadata.getOrganization() != null //
436 && !StringUtils.equalsIgnoreCase( sharedModel.getOrganization().getName(),
437 versionMetadata.getOrganization().getName() ) )
439 sharedModel.setOrganization( versionMetadata.getOrganization() );
442 if ( sharedModel.getUrl() != null //
443 && !StringUtils.equalsIgnoreCase( sharedModel.getUrl(), versionMetadata.getUrl() ) )
445 sharedModel.setUrl( versionMetadata.getUrl() );
449 isFirstVersion = false;
453 catch ( MetadataResolutionException e )
455 throw new ArchivaRestServiceException( e.getMessage(),
456 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
460 if ( repositorySession != null )
462 repositorySession.close();
468 public List<TreeEntry> getTreeEntries( String groupId, String artifactId, String version, String repositoryId )
469 throws ArchivaRestServiceException
471 List<String> selectedRepos = getSelectedRepos( repositoryId );
475 return dependencyTreeBuilder.buildDependencyTree( selectedRepos, groupId, artifactId, version );
477 catch ( Exception e )
479 log.error( e.getMessage(), e );
482 return Collections.emptyList();
486 public List<ManagedRepository> getUserRepositories()
487 throws ArchivaRestServiceException
491 return userRepositories.getAccessibleRepositories( getPrincipal() );
493 catch ( ArchivaSecurityException e )
495 throw new ArchivaRestServiceException( "repositories.read.observable.error",
496 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
501 public List<ManagedRepository> getUserManagableRepositories() throws ArchivaRestServiceException {
504 return userRepositories.getManagableRepositories( getPrincipal() );
506 catch ( ArchivaSecurityException e )
508 throw new ArchivaRestServiceException( "repositories.read.managable.error",
509 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
514 public List<Artifact> getDependees( String groupId, String artifactId, String version, String repositoryId )
515 throws ArchivaRestServiceException
517 List<ProjectVersionReference> references = new ArrayList<>();
518 // TODO: what if we get duplicates across repositories?
519 RepositorySession repositorySession = repositorySessionFactory.createSession();
522 MetadataResolver metadataResolver = repositorySession.getResolver();
523 for ( String repoId : getObservableRepos() )
525 // TODO: what about if we want to see this irrespective of version?
527 metadataResolver.resolveProjectReferences( repositorySession, repoId, groupId, artifactId,
531 catch ( MetadataResolutionException e )
533 throw new ArchivaRestServiceException( e.getMessage(),
534 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
538 repositorySession.close();
541 List<Artifact> artifacts = new ArrayList<>( references.size() );
543 for ( ProjectVersionReference projectVersionReference : references )
545 artifacts.add( new Artifact( projectVersionReference.getNamespace(), projectVersionReference.getProjectId(),
546 projectVersionReference.getProjectVersion() ) );
552 public List<Entry> getMetadatas( String groupId, String artifactId, String version, String repositoryId )
553 throws ArchivaRestServiceException
555 ProjectVersionMetadata projectVersionMetadata =
556 getProjectMetadata( groupId, artifactId, version, repositoryId );
557 if ( projectVersionMetadata == null )
559 return Collections.emptyList();
561 MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
563 if ( metadataFacet == null )
565 return Collections.emptyList();
567 Map<String, String> map = metadataFacet.toProperties();
568 List<Entry> entries = new ArrayList<>( map.size() );
570 for ( Map.Entry<String, String> entry : map.entrySet() )
572 entries.add( new Entry( entry.getKey(), entry.getValue() ) );
579 public Boolean addMetadata( String groupId, String artifactId, String version, String key, String value,
580 String repositoryId )
581 throws ArchivaRestServiceException
583 ProjectVersionMetadata projectVersionMetadata =
584 getProjectMetadata( groupId, artifactId, version, repositoryId );
586 if ( projectVersionMetadata == null )
588 return Boolean.FALSE;
591 Map<String, String> properties = new HashMap<>();
593 MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
595 if ( metadataFacet != null && metadataFacet.toProperties() != null )
597 properties.putAll( metadataFacet.toProperties() );
601 metadataFacet = new GenericMetadataFacet();
604 properties.put( key, value );
606 metadataFacet.fromProperties( properties );
608 projectVersionMetadata.addFacet( metadataFacet );
610 RepositorySession repositorySession = repositorySessionFactory.createSession();
614 MetadataRepository metadataRepository = repositorySession.getRepository();
616 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
618 repositorySession.save();
620 catch ( MetadataRepositoryException e )
622 log.error( e.getMessage(), e );
623 throw new ArchivaRestServiceException( e.getMessage(),
624 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
628 repositorySession.close();
634 public Boolean deleteMetadata( String groupId, String artifactId, String version, String key, String repositoryId )
635 throws ArchivaRestServiceException
637 ProjectVersionMetadata projectVersionMetadata =
638 getProjectMetadata( groupId, artifactId, version, repositoryId );
640 if ( projectVersionMetadata == null )
642 return Boolean.FALSE;
645 GenericMetadataFacet metadataFacet =
646 (GenericMetadataFacet) projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
648 if ( metadataFacet != null && metadataFacet.toProperties() != null )
650 Map<String, String> properties = metadataFacet.toProperties();
651 properties.remove( key );
652 metadataFacet.setAdditionalProperties( properties );
659 RepositorySession repositorySession = repositorySessionFactory.createSession();
663 MetadataRepository metadataRepository = repositorySession.getRepository();
665 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
667 repositorySession.save();
669 catch ( MetadataRepositoryException e )
671 log.error( e.getMessage(), e );
672 throw new ArchivaRestServiceException( e.getMessage(),
673 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
677 repositorySession.close();
683 public List<ArtifactContentEntry> getArtifactContentEntries( String groupId, String artifactId, String version,
684 String classifier, String type, String path,
685 String repositoryId )
686 throws ArchivaRestServiceException
688 List<String> selectedRepos = getSelectedRepos( repositoryId );
691 for ( String repoId : selectedRepos )
694 ManagedRepositoryContent managedRepositoryContent =
695 getManagedRepositoryContent( repoId );
696 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, classifier,
697 StringUtils.isEmpty( type ) ? "jar" : type,
699 Path file = managedRepositoryContent.toFile( archivaArtifact );
700 if ( Files.exists(file) )
702 return readFileEntries( file, path, repoId );
706 catch ( IOException e )
708 log.error( e.getMessage(), e );
709 throw new ArchivaRestServiceException( e.getMessage(),
710 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
712 catch ( RepositoryNotFoundException e )
714 log.error( e.getMessage(), e );
715 throw new ArchivaRestServiceException( e.getMessage(),
716 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
718 catch ( RepositoryException e )
720 log.error( e.getMessage(), e );
721 throw new ArchivaRestServiceException( e.getMessage(),
722 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
724 return Collections.emptyList();
728 public List<Artifact> getArtifactDownloadInfos( String groupId, String artifactId, String version,
729 String repositoryId )
730 throws ArchivaRestServiceException
732 List<String> selectedRepos = getSelectedRepos( repositoryId );
734 List<Artifact> artifactDownloadInfos = new ArrayList<>();
736 try (RepositorySession session = repositorySessionFactory.createSession())
738 MetadataResolver metadataResolver = session.getResolver();
739 for ( String repoId : selectedRepos )
741 List<ArtifactMetadata> artifacts = new ArrayList<>(
742 metadataResolver.resolveArtifacts( session, repoId, groupId, artifactId, version ) );
743 Collections.sort( artifacts, ArtifactMetadataVersionComparator.INSTANCE );
744 if ( artifacts != null && !artifacts.isEmpty() )
746 return buildArtifacts( artifacts, repoId );
750 catch ( MetadataResolutionException e )
752 log.error( e.getMessage(), e );
753 throw new ArchivaRestServiceException( e.getMessage(),
754 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
757 return artifactDownloadInfos;
761 public ArtifactContent getArtifactContentText( String groupId, String artifactId, String version, String classifier,
762 String type, String path, String repositoryId )
763 throws ArchivaRestServiceException
765 List<String> selectedRepos = getSelectedRepos( repositoryId );
768 for ( String repoId : selectedRepos )
771 ManagedRepositoryContent managedRepositoryContent = null;
774 managedRepositoryContent = getManagedRepositoryContent( repoId );
776 catch ( RepositoryException e )
778 log.error("No repository content found for "+repoId);
781 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, classifier,
782 StringUtils.isEmpty( type ) ? "jar" : type,
784 Path file = managedRepositoryContent.toFile( archivaArtifact );
785 if ( !Files.exists(file) )
787 log.debug( "file: {} not exists for repository: {} try next repository", file, repoId );
790 if ( StringUtils.isNotBlank( path ) )
792 // zip entry of the path -> path must a real file entry of the archive
793 JarFile jarFile = new JarFile( file.toFile() );
794 ZipEntry zipEntry = jarFile.getEntry( path );
795 try (InputStream inputStream = jarFile.getInputStream( zipEntry ))
797 return new ArtifactContent( IOUtils.toString( inputStream, ARTIFACT_CONTENT_ENCODING ), repoId );
801 closeQuietly( jarFile );
804 return new ArtifactContent( new String(Files.readAllBytes( file ), ARTIFACT_CONTENT_ENCODING), repoId );
807 catch ( IOException e )
809 log.error( e.getMessage(), e );
810 throw new ArchivaRestServiceException( e.getMessage(),
811 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
813 log.debug( "artifact: {}:{}:{}:{}:{} not found", groupId, artifactId, version, classifier, type );
815 return new ArtifactContent();
819 public Boolean artifactAvailable( String groupId, String artifactId, String version, String classifier,
820 String repositoryId )
821 throws ArchivaRestServiceException
823 List<String> selectedRepos = getSelectedRepos( repositoryId );
825 boolean snapshot = VersionUtil.isSnapshot( version );
829 for ( String repoId : selectedRepos )
832 org.apache.archiva.repository.ManagedRepository managedRepo = repositoryRegistry.getManagedRepository(repoId);
833 if (!proxyRegistry.hasHandler(managedRepo.getType())) {
834 throw new RepositoryException( "No proxy handler found for repository type "+managedRepo.getType());
836 RepositoryProxyHandler proxyHandler = proxyRegistry.getHandler(managedRepo.getType()).get(0);
837 if ( ( snapshot && !managedRepo.getActiveReleaseSchemes().contains(ReleaseScheme.SNAPSHOT) ) || ( !snapshot
838 && managedRepo.getActiveReleaseSchemes().contains(ReleaseScheme.SNAPSHOT) ) )
842 ManagedRepositoryContent managedRepositoryContent = getManagedRepositoryContent( repoId );
844 // FIXME default to jar which can be wrong for war zip etc....
845 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version,
846 StringUtils.isEmpty( classifier )
848 : classifier, "jar", repoId );
849 Path file = managedRepositoryContent.toFile( archivaArtifact );
851 if ( file != null && Files.exists(file) )
856 // in case of SNAPSHOT we can have timestamped version locally !
857 if ( StringUtils.endsWith( version, VersionUtil.SNAPSHOT ) )
859 Path metadataFile = file.getParent().resolve(MetadataTools.MAVEN_METADATA );
860 if ( Files.exists(metadataFile) )
864 ArchivaRepositoryMetadata archivaRepositoryMetadata =
865 MavenMetadataReader.read( metadataFile );
866 int buildNumber = archivaRepositoryMetadata.getSnapshotVersion().getBuildNumber();
867 String timeStamp = archivaRepositoryMetadata.getSnapshotVersion().getTimestamp();
868 // rebuild file name with timestamped version and build number
869 String timeStampFileName = new StringBuilder( artifactId ).append( '-' ) //
870 .append( StringUtils.remove( version, "-" + VersionUtil.SNAPSHOT ) ) //
871 .append( '-' ).append( timeStamp ) //
872 .append( '-' ).append( Integer.toString( buildNumber ) ) //
873 .append( ( StringUtils.isEmpty( classifier ) ? "" : "-" + classifier ) ) //
874 .append( ".jar" ).toString();
876 Path timeStampFile = file.getParent().resolve( timeStampFileName );
877 log.debug( "try to find timestamped snapshot version file: {}", timeStampFile.toAbsolutePath() );
878 if ( Files.exists(timeStampFile) )
883 catch ( XMLException e )
885 log.warn( "skip fail to find timestamped snapshot file: {}", e.getMessage() );
890 String path = managedRepositoryContent.toPath( archivaArtifact );
892 file = proxyHandler.fetchFromProxies( managedRepositoryContent, path );
894 if ( file != null && Files.exists(file) )
897 String pomPath = StringUtils.substringBeforeLast( path, ".jar" ) + ".pom";
898 proxyHandler.fetchFromProxies( managedRepositoryContent, pomPath );
902 } catch ( RepositoryException e )
904 log.error( e.getMessage(), e );
905 throw new ArchivaRestServiceException( e.getMessage(),
906 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
913 public Boolean artifactAvailable( String groupId, String artifactId, String version, String repositoryId )
914 throws ArchivaRestServiceException
916 return artifactAvailable( groupId, artifactId, version, null, repositoryId );
920 public List<Artifact> getArtifacts( String repositoryId )
921 throws ArchivaRestServiceException
923 RepositorySession repositorySession = repositorySessionFactory.createSession();
926 List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifacts( repositoryId );
927 return buildArtifacts( artifactMetadatas, repositoryId );
929 catch ( MetadataRepositoryException e )
931 throw new ArchivaRestServiceException( e.getMessage(), e );
935 repositorySession.close();
940 public List<Artifact> getArtifactsByProjectVersionMetadata( String key, String value, String repositoryId )
941 throws ArchivaRestServiceException
943 RepositorySession repositorySession = repositorySessionFactory.createSession();
946 List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifactsByProjectVersionMetadata( key, value, repositoryId );
947 return buildArtifacts( artifactMetadatas, repositoryId );
949 catch ( MetadataRepositoryException e )
951 throw new ArchivaRestServiceException( e.getMessage(), e );
955 repositorySession.close();
960 public List<Artifact> getArtifactsByMetadata( String key, String value, String repositoryId )
961 throws ArchivaRestServiceException
963 RepositorySession repositorySession = repositorySessionFactory.createSession();
966 List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifactsByMetadata( key, value, repositoryId );
967 return buildArtifacts( artifactMetadatas, repositoryId );
969 catch ( MetadataRepositoryException e )
971 throw new ArchivaRestServiceException( e.getMessage(), e );
975 repositorySession.close();
980 public List<Artifact> getArtifactsByProperty( String key, String value, String repositoryId )
981 throws ArchivaRestServiceException
983 RepositorySession repositorySession = repositorySessionFactory.createSession();
986 List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifactsByProperty( key, value, repositoryId );
987 return buildArtifacts( artifactMetadatas, repositoryId );
989 catch ( MetadataRepositoryException e )
991 throw new ArchivaRestServiceException( e.getMessage(), e );
995 repositorySession.close();
1000 public Boolean importMetadata( MetadataAddRequest metadataAddRequest, String repositoryId )
1001 throws ArchivaRestServiceException
1003 boolean result = true;
1004 for ( Map.Entry<String, String> metadata : metadataAddRequest.getMetadatas().entrySet() )
1006 result = addMetadata( metadataAddRequest.getGroupId(), metadataAddRequest.getArtifactId(),
1007 metadataAddRequest.getVersion(), metadata.getKey(), metadata.getValue(),
1018 public List<Artifact> searchArtifacts( String text, String repositoryId, Boolean exact )
1019 throws ArchivaRestServiceException
1021 RepositorySession repositorySession = repositorySessionFactory.createSession();
1024 List<ArtifactMetadata> artifactMetadatas =
1025 repositorySession.getRepository().searchArtifacts( text, repositoryId, exact == null ? false : exact );
1026 return buildArtifacts( artifactMetadatas, repositoryId );
1028 catch ( MetadataRepositoryException e )
1030 throw new ArchivaRestServiceException( e.getMessage(), e );
1034 repositorySession.close();
1039 public List<Artifact> searchArtifacts( String key, String text, String repositoryId, Boolean exact )
1040 throws ArchivaRestServiceException
1042 RepositorySession repositorySession = repositorySessionFactory.createSession();
1045 List<ArtifactMetadata> artifactMetadatas =
1046 repositorySession.getRepository().searchArtifacts( key, text, repositoryId, exact == null ? false : exact );
1047 return buildArtifacts( artifactMetadatas, repositoryId );
1049 catch ( MetadataRepositoryException e )
1051 throw new ArchivaRestServiceException( e.getMessage(), e );
1055 repositorySession.close();
1059 //---------------------------
1061 //---------------------------
1063 private void closeQuietly( JarFile jarFile )
1065 if ( jarFile != null )
1071 catch ( IOException e )
1073 log.warn( "ignore error closing jarFile {}", jarFile.getName() );
1078 protected List<ArtifactContentEntry> readFileEntries(final Path file, final String filterPath, final String repoId )
1081 String cleanedfilterPath = filterPath==null ? "" : (StringUtils.startsWith(filterPath, "/") ?
1082 StringUtils.substringAfter(filterPath, "/") : filterPath);
1083 Map<String, ArtifactContentEntry> artifactContentEntryMap = new HashMap<>();
1084 int filterDepth = StringUtils.countMatches( cleanedfilterPath, "/" );
1085 if (!StringUtils.endsWith(cleanedfilterPath,"/") && !StringUtils.isEmpty(cleanedfilterPath)) {
1088 JarFile jarFile = new JarFile( file.toFile() );
1091 Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
1092 while ( jarEntryEnumeration.hasMoreElements() )
1094 JarEntry currentEntry = jarEntryEnumeration.nextElement();
1095 String cleanedEntryName = StringUtils.endsWith( currentEntry.getName(), "/" ) ? //
1096 StringUtils.substringBeforeLast( currentEntry.getName(), "/" ) : currentEntry.getName();
1097 String entryRootPath = getRootPath( cleanedEntryName );
1098 int depth = StringUtils.countMatches( cleanedEntryName, "/" );
1099 if ( StringUtils.isEmpty( cleanedfilterPath ) //
1100 && !artifactContentEntryMap.containsKey( entryRootPath ) //
1101 && depth == filterDepth )
1104 artifactContentEntryMap.put( entryRootPath,
1105 new ArtifactContentEntry( entryRootPath, !currentEntry.isDirectory(),
1110 if ( StringUtils.startsWith( cleanedEntryName, cleanedfilterPath ) //
1111 && ( depth == filterDepth || ( !currentEntry.isDirectory() && depth == filterDepth ) ) )
1113 artifactContentEntryMap.put( cleanedEntryName, new ArtifactContentEntry( cleanedEntryName,
1114 !currentEntry.isDirectory(),
1120 if ( StringUtils.isNotEmpty( cleanedfilterPath ) )
1122 Map<String, ArtifactContentEntry> filteredArtifactContentEntryMap = new HashMap<>();
1124 for ( Map.Entry<String, ArtifactContentEntry> entry : artifactContentEntryMap.entrySet() )
1126 filteredArtifactContentEntryMap.put( entry.getKey(), entry.getValue() );
1129 List<ArtifactContentEntry> sorted = getSmallerDepthEntries( filteredArtifactContentEntryMap );
1130 if ( sorted == null )
1132 return Collections.emptyList();
1134 Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
1140 if ( jarFile != null )
1145 List<ArtifactContentEntry> sorted = new ArrayList<>( artifactContentEntryMap.values() );
1146 Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
1150 private List<ArtifactContentEntry> getSmallerDepthEntries( Map<String, ArtifactContentEntry> entries )
1152 int smallestDepth = Integer.MAX_VALUE;
1153 Map<Integer, List<ArtifactContentEntry>> perDepthList = new HashMap<>();
1154 for ( Map.Entry<String, ArtifactContentEntry> entry : entries.entrySet() )
1157 ArtifactContentEntry current = entry.getValue();
1159 if ( current.getDepth() < smallestDepth )
1161 smallestDepth = current.getDepth();
1164 List<ArtifactContentEntry> currentList = perDepthList.get( current.getDepth() );
1166 if ( currentList == null )
1168 currentList = new ArrayList<>();
1169 currentList.add( current );
1170 perDepthList.put( current.getDepth(), currentList );
1174 currentList.add( current );
1179 return perDepthList.get( smallestDepth );
1184 * @return org/apache -> org , org -> org
1186 private String getRootPath( String path )
1188 if ( StringUtils.contains( path, '/' ) )
1190 return StringUtils.substringBefore( path, "/" );
1195 private List<String> getSelectedRepos( String repositoryId )
1196 throws ArchivaRestServiceException
1199 List<String> selectedRepos = getObservableRepos();
1201 if ( CollectionUtils.isEmpty( selectedRepos ) )
1203 return Collections.emptyList();
1206 if ( StringUtils.isNotEmpty( repositoryId ) )
1208 // check user has karma on the repository
1209 if ( !selectedRepos.contains( repositoryId ) )
1211 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
1212 Response.Status.FORBIDDEN.getStatusCode(), null );
1214 selectedRepos = Collections.singletonList( repositoryId );
1216 return selectedRepos;
1220 private String collapseNamespaces( RepositorySession repositorySession, MetadataResolver metadataResolver,
1221 Collection<String> repoIds, String n )
1222 throws MetadataResolutionException
1224 Set<String> subNamespaces = new LinkedHashSet<String>();
1225 for ( String repoId : repoIds )
1227 subNamespaces.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, n ) );
1229 if ( subNamespaces.size() != 1 )
1231 log.debug( "{} is not collapsible as it has sub-namespaces: {}", n, subNamespaces );
1236 for ( String repoId : repoIds )
1238 Collection<String> projects = metadataResolver.resolveProjects( repositorySession, repoId, n );
1239 if ( projects != null && !projects.isEmpty() )
1241 log.debug( "{} is not collapsible as it has projects", n );
1245 return collapseNamespaces( repositorySession, metadataResolver, repoIds,
1246 n + "." + subNamespaces.iterator().next() );
1250 public Cache<String, ProjectVersionMetadata> getVersionMetadataCache()
1252 return versionMetadataCache;
1255 public void setVersionMetadataCache( Cache<String, ProjectVersionMetadata> versionMetadataCache )
1257 this.versionMetadataCache = versionMetadataCache;