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.RepositoryAdminException;
22 import org.apache.archiva.admin.model.beans.ManagedRepository;
23 import org.apache.archiva.common.utils.VersionComparator;
24 import org.apache.archiva.common.utils.VersionUtil;
25 import org.apache.archiva.dependency.tree.maven2.DependencyTreeBuilder;
26 import org.apache.archiva.maven2.metadata.MavenMetadataReader;
27 import org.apache.archiva.maven2.model.Artifact;
28 import org.apache.archiva.maven2.model.TreeEntry;
29 import org.apache.archiva.metadata.generic.GenericMetadataFacet;
30 import org.apache.archiva.metadata.model.ArtifactMetadata;
31 import org.apache.archiva.metadata.model.MetadataFacet;
32 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
33 import org.apache.archiva.metadata.model.ProjectVersionReference;
34 import org.apache.archiva.metadata.repository.*;
35 import org.apache.archiva.metadata.repository.storage.maven2.ArtifactMetadataVersionComparator;
36 import org.apache.archiva.metadata.repository.storage.maven2.MavenProjectFacet;
37 import org.apache.archiva.model.ArchivaArtifact;
38 import org.apache.archiva.model.ArchivaRepositoryMetadata;
39 import org.apache.archiva.proxy.model.RepositoryProxyConnectors;
40 import org.apache.archiva.redback.components.cache.Cache;
41 import org.apache.archiva.repository.ManagedRepositoryContent;
42 import org.apache.archiva.repository.RepositoryContentFactory;
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.collections.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 Charset ARTIFACT_CONTENT_ENCODING=Charset.forName( "UTF-8" );
83 private DependencyTreeBuilder dependencyTreeBuilder;
86 private RepositoryContentFactory repositoryContentFactory;
89 @Named( value = "repositoryProxyConnectors#default" )
90 private RepositoryProxyConnectors connectors;
93 @Named( value = "browse#versionMetadata" )
94 private Cache<String, ProjectVersionMetadata> versionMetadataCache;
97 public BrowseResult getRootGroups( String repositoryId )
98 throws ArchivaRestServiceException
100 List<String> selectedRepos = getSelectedRepos( repositoryId );
102 Set<String> namespaces = new LinkedHashSet<String>();
104 // TODO: this logic should be optional, particularly remembering we want to keep this code simple
105 // it is located here to avoid the content repository implementation needing to do too much for what
106 // is essentially presentation code
107 Set<String> namespacesToCollapse = new LinkedHashSet<String>();
108 RepositorySession repositorySession = repositorySessionFactory.createSession();
111 MetadataResolver metadataResolver = repositorySession.getResolver();
113 for ( String repoId : selectedRepos )
115 namespacesToCollapse.addAll( metadataResolver.resolveRootNamespaces( repositorySession, repoId ) );
117 for ( String n : namespacesToCollapse )
119 // TODO: check performance of this
120 namespaces.add( collapseNamespaces( repositorySession, metadataResolver, selectedRepos, n ) );
123 catch ( MetadataResolutionException e )
125 throw new ArchivaRestServiceException( e.getMessage(),
126 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
130 repositorySession.close();
133 List<BrowseResultEntry> browseGroupResultEntries = new ArrayList<>( namespaces.size() );
134 for ( String namespace : namespaces )
136 browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
139 Collections.sort( browseGroupResultEntries );
140 return new BrowseResult( browseGroupResultEntries );
144 public BrowseResult browseGroupId( String groupId, String repositoryId )
145 throws ArchivaRestServiceException
147 List<String> selectedRepos = getSelectedRepos( repositoryId );
149 Set<String> projects = new LinkedHashSet<>();
151 RepositorySession repositorySession = repositorySessionFactory.createSession();
152 Set<String> namespaces;
155 MetadataResolver metadataResolver = repositorySession.getResolver();
157 Set<String> namespacesToCollapse = new LinkedHashSet<>();
158 for ( String repoId : selectedRepos )
160 namespacesToCollapse.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, groupId ) );
162 projects.addAll( metadataResolver.resolveProjects( repositorySession, repoId, groupId ) );
165 // TODO: this logic should be optional, particularly remembering we want to keep this code simple
166 // it is located here to avoid the content repository implementation needing to do too much for what
167 // is essentially presentation code
168 namespaces = new LinkedHashSet<>();
169 for ( String n : namespacesToCollapse )
171 // TODO: check performance of this
173 collapseNamespaces( repositorySession, metadataResolver, selectedRepos, groupId + "." + n ) );
176 catch ( MetadataResolutionException e )
178 throw new ArchivaRestServiceException( e.getMessage(),
179 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
183 repositorySession.close();
185 List<BrowseResultEntry> browseGroupResultEntries = new ArrayList<>( namespaces.size() + projects.size() );
186 for ( String namespace : namespaces )
188 browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ).groupId( namespace ) );
190 for ( String project : projects )
192 browseGroupResultEntries.add(
193 new BrowseResultEntry( groupId + '.' + project, true ).groupId( groupId ).artifactId( project ) );
195 Collections.sort( browseGroupResultEntries );
196 return new BrowseResult( browseGroupResultEntries );
201 public VersionsList getVersionsList( String groupId, String artifactId, String repositoryId )
202 throws ArchivaRestServiceException
204 List<String> selectedRepos = getSelectedRepos( repositoryId );
208 Collection<String> versions = getVersions( selectedRepos, groupId, artifactId );
209 return new VersionsList( new ArrayList<>( versions ) );
211 catch ( MetadataResolutionException e )
213 throw new ArchivaRestServiceException( e.getMessage(),
214 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
219 private Collection<String> getVersions( List<String> selectedRepos, String groupId, String artifactId )
220 throws MetadataResolutionException
223 RepositorySession repositorySession = repositorySessionFactory.createSession();
226 MetadataResolver metadataResolver = repositorySession.getResolver();
228 Set<String> versions = new LinkedHashSet<String>();
230 for ( String repoId : selectedRepos )
232 Collection<String> projectVersions =
233 metadataResolver.resolveProjectVersions( repositorySession, repoId, groupId, artifactId );
234 versions.addAll( projectVersions );
237 List<String> sortedVersions = new ArrayList<>( versions );
239 Collections.sort( sortedVersions, VersionComparator.getInstance() );
241 return sortedVersions;
245 repositorySession.close();
250 public ProjectVersionMetadata getProjectMetadata( String groupId, String artifactId, String version,
251 String repositoryId )
252 throws ArchivaRestServiceException
254 List<String> selectedRepos = getSelectedRepos( repositoryId );
256 RepositorySession repositorySession = null;
259 repositorySession = repositorySessionFactory.createSession();
261 MetadataResolver metadataResolver = repositorySession.getResolver();
263 ProjectVersionMetadata versionMetadata = null;
264 for ( String repoId : selectedRepos )
266 if ( versionMetadata == null || versionMetadata.isIncomplete() )
270 ProjectVersionMetadata versionMetadataTmp =
271 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
274 if ( versionMetadata == null && versionMetadataTmp != null )
276 versionMetadata = versionMetadataTmp;
281 catch ( MetadataResolutionException e )
283 log.warn( "Skipping invalid metadata while compiling shared model for {}:{} in repo {}: {}",
284 groupId, artifactId, repoId, e.getMessage() );
289 return versionMetadata;
293 if ( repositorySession != null )
295 repositorySession.close();
302 public ProjectVersionMetadata getProjectVersionMetadata( String groupId, String artifactId, String repositoryId )
303 throws ArchivaRestServiceException
306 List<String> selectedRepos = getSelectedRepos( repositoryId );
308 RepositorySession repositorySession = null;
312 Collection<String> projectVersions = getVersions( selectedRepos, groupId, artifactId );
314 repositorySession = repositorySessionFactory.createSession();
316 MetadataResolver metadataResolver = repositorySession.getResolver();
318 ProjectVersionMetadata sharedModel = new ProjectVersionMetadata();
320 MavenProjectFacet mavenFacet = new MavenProjectFacet();
321 mavenFacet.setGroupId( groupId );
322 mavenFacet.setArtifactId( artifactId );
323 sharedModel.addFacet( mavenFacet );
325 boolean isFirstVersion = true;
327 for ( String version : projectVersions )
329 ProjectVersionMetadata versionMetadata = null;
330 for ( String repoId : selectedRepos )
332 if ( versionMetadata == null || versionMetadata.isIncomplete() )
336 ProjectVersionMetadata projectVersionMetadataResolved = null;
337 boolean useCache = !StringUtils.endsWith( version, VersionUtil.SNAPSHOT );
338 String cacheKey = null;
339 boolean cacheToUpdate = false;
340 // FIXME a bit maven centric!!!
341 // not a snapshot so get it from cache
344 cacheKey = repoId + groupId + artifactId + version;
345 projectVersionMetadataResolved = versionMetadataCache.get( cacheKey );
347 if ( useCache && projectVersionMetadataResolved != null )
349 versionMetadata = projectVersionMetadataResolved;
353 projectVersionMetadataResolved =
354 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId,
355 artifactId, version );
356 versionMetadata = projectVersionMetadataResolved;
357 cacheToUpdate = true;
360 if ( useCache && cacheToUpdate )
362 versionMetadataCache.put( cacheKey, projectVersionMetadataResolved );
366 catch ( MetadataResolutionException e )
368 log.error( "Skipping invalid metadata while compiling shared model for " + groupId + ":"
369 + artifactId + " in repo " + repoId + ": " + e.getMessage() );
374 if ( versionMetadata == null )
379 if ( isFirstVersion )
381 sharedModel = versionMetadata;
382 sharedModel.setId( null );
386 MavenProjectFacet versionMetadataMavenFacet =
387 (MavenProjectFacet) versionMetadata.getFacet( MavenProjectFacet.FACET_ID );
388 if ( versionMetadataMavenFacet != null )
390 if ( mavenFacet.getPackaging() != null //
391 && !StringUtils.equalsIgnoreCase( mavenFacet.getPackaging(),
392 versionMetadataMavenFacet.getPackaging() ) )
394 mavenFacet.setPackaging( null );
398 if ( StringUtils.isEmpty( sharedModel.getName() ) //
399 && !StringUtils.isEmpty( versionMetadata.getName() ) )
401 sharedModel.setName( versionMetadata.getName() );
404 if ( sharedModel.getDescription() != null //
405 && !StringUtils.equalsIgnoreCase( sharedModel.getDescription(),
406 versionMetadata.getDescription() ) )
408 sharedModel.setDescription( StringUtils.isNotEmpty( versionMetadata.getDescription() )
409 ? versionMetadata.getDescription()
413 if ( sharedModel.getIssueManagement() != null //
414 && versionMetadata.getIssueManagement() != null //
415 && !StringUtils.equalsIgnoreCase( sharedModel.getIssueManagement().getUrl(),
416 versionMetadata.getIssueManagement().getUrl() ) )
418 sharedModel.setIssueManagement( versionMetadata.getIssueManagement() );
421 if ( sharedModel.getCiManagement() != null //
422 && versionMetadata.getCiManagement() != null //
423 && !StringUtils.equalsIgnoreCase( sharedModel.getCiManagement().getUrl(),
424 versionMetadata.getCiManagement().getUrl() ) )
426 sharedModel.setCiManagement( versionMetadata.getCiManagement() );
429 if ( sharedModel.getOrganization() != null //
430 && versionMetadata.getOrganization() != null //
431 && !StringUtils.equalsIgnoreCase( sharedModel.getOrganization().getName(),
432 versionMetadata.getOrganization().getName() ) )
434 sharedModel.setOrganization( versionMetadata.getOrganization() );
437 if ( sharedModel.getUrl() != null //
438 && !StringUtils.equalsIgnoreCase( sharedModel.getUrl(), versionMetadata.getUrl() ) )
440 sharedModel.setUrl( versionMetadata.getUrl() );
444 isFirstVersion = false;
448 catch ( MetadataResolutionException e )
450 throw new ArchivaRestServiceException( e.getMessage(),
451 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
455 if ( repositorySession != null )
457 repositorySession.close();
463 public List<TreeEntry> getTreeEntries( String groupId, String artifactId, String version, String repositoryId )
464 throws ArchivaRestServiceException
466 List<String> selectedRepos = getSelectedRepos( repositoryId );
470 return dependencyTreeBuilder.buildDependencyTree( selectedRepos, groupId, artifactId, version );
472 catch ( Exception e )
474 log.error( e.getMessage(), e );
477 return Collections.emptyList();
481 public List<ManagedRepository> getUserRepositories()
482 throws ArchivaRestServiceException
486 return userRepositories.getAccessibleRepositories( getPrincipal() );
488 catch ( ArchivaSecurityException e )
490 throw new ArchivaRestServiceException( "repositories.read.observable.error",
491 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
496 public List<ManagedRepository> getUserManagableRepositories() throws ArchivaRestServiceException {
499 return userRepositories.getManagableRepositories( getPrincipal() );
501 catch ( ArchivaSecurityException e )
503 throw new ArchivaRestServiceException( "repositories.read.managable.error",
504 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
509 public List<Artifact> getDependees( String groupId, String artifactId, String version, String repositoryId )
510 throws ArchivaRestServiceException
512 List<ProjectVersionReference> references = new ArrayList<>();
513 // TODO: what if we get duplicates across repositories?
514 RepositorySession repositorySession = repositorySessionFactory.createSession();
517 MetadataResolver metadataResolver = repositorySession.getResolver();
518 for ( String repoId : getObservableRepos() )
520 // TODO: what about if we want to see this irrespective of version?
522 metadataResolver.resolveProjectReferences( repositorySession, repoId, groupId, artifactId,
526 catch ( MetadataResolutionException e )
528 throw new ArchivaRestServiceException( e.getMessage(),
529 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
533 repositorySession.close();
536 List<Artifact> artifacts = new ArrayList<>( references.size() );
538 for ( ProjectVersionReference projectVersionReference : references )
540 artifacts.add( new Artifact( projectVersionReference.getNamespace(), projectVersionReference.getProjectId(),
541 projectVersionReference.getProjectVersion() ) );
547 public List<Entry> getMetadatas( String groupId, String artifactId, String version, String repositoryId )
548 throws ArchivaRestServiceException
550 ProjectVersionMetadata projectVersionMetadata =
551 getProjectMetadata( groupId, artifactId, version, repositoryId );
552 if ( projectVersionMetadata == null )
554 return Collections.emptyList();
556 MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
558 if ( metadataFacet == null )
560 return Collections.emptyList();
562 Map<String, String> map = metadataFacet.toProperties();
563 List<Entry> entries = new ArrayList<>( map.size() );
565 for ( Map.Entry<String, String> entry : map.entrySet() )
567 entries.add( new Entry( entry.getKey(), entry.getValue() ) );
574 public Boolean addMetadata( String groupId, String artifactId, String version, String key, String value,
575 String repositoryId )
576 throws ArchivaRestServiceException
578 ProjectVersionMetadata projectVersionMetadata =
579 getProjectMetadata( groupId, artifactId, version, repositoryId );
581 if ( projectVersionMetadata == null )
583 return Boolean.FALSE;
586 Map<String, String> properties = new HashMap<>();
588 MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
590 if ( metadataFacet != null && metadataFacet.toProperties() != null )
592 properties.putAll( metadataFacet.toProperties() );
596 metadataFacet = new GenericMetadataFacet();
599 properties.put( key, value );
601 metadataFacet.fromProperties( properties );
603 projectVersionMetadata.addFacet( metadataFacet );
605 RepositorySession repositorySession = repositorySessionFactory.createSession();
609 MetadataRepository metadataRepository = repositorySession.getRepository();
611 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
613 repositorySession.save();
615 catch ( MetadataRepositoryException e )
617 log.error( e.getMessage(), e );
618 throw new ArchivaRestServiceException( e.getMessage(),
619 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
623 repositorySession.close();
629 public Boolean deleteMetadata( String groupId, String artifactId, String version, String key, String repositoryId )
630 throws ArchivaRestServiceException
632 ProjectVersionMetadata projectVersionMetadata =
633 getProjectMetadata( groupId, artifactId, version, repositoryId );
635 if ( projectVersionMetadata == null )
637 return Boolean.FALSE;
640 GenericMetadataFacet metadataFacet =
641 (GenericMetadataFacet) projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
643 if ( metadataFacet != null && metadataFacet.toProperties() != null )
645 Map<String, String> properties = metadataFacet.toProperties();
646 properties.remove( key );
647 metadataFacet.setAdditionalProperties( properties );
654 RepositorySession repositorySession = repositorySessionFactory.createSession();
658 MetadataRepository metadataRepository = repositorySession.getRepository();
660 metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
662 repositorySession.save();
664 catch ( MetadataRepositoryException e )
666 log.error( e.getMessage(), e );
667 throw new ArchivaRestServiceException( e.getMessage(),
668 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
672 repositorySession.close();
678 public List<ArtifactContentEntry> getArtifactContentEntries( String groupId, String artifactId, String version,
679 String classifier, String type, String path,
680 String repositoryId )
681 throws ArchivaRestServiceException
683 List<String> selectedRepos = getSelectedRepos( repositoryId );
686 for ( String repoId : selectedRepos )
689 ManagedRepositoryContent managedRepositoryContent =
690 repositoryContentFactory.getManagedRepositoryContent( repoId );
691 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, classifier,
692 StringUtils.isEmpty( type ) ? "jar" : type,
694 Path file = managedRepositoryContent.toFile( archivaArtifact );
695 if ( Files.exists(file) )
697 return readFileEntries( file, path, repoId );
701 catch ( IOException e )
703 log.error( e.getMessage(), e );
704 throw new ArchivaRestServiceException( e.getMessage(),
705 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
707 catch ( RepositoryNotFoundException e )
709 log.error( e.getMessage(), e );
710 throw new ArchivaRestServiceException( e.getMessage(),
711 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
713 catch ( RepositoryException e )
715 log.error( e.getMessage(), e );
716 throw new ArchivaRestServiceException( e.getMessage(),
717 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
719 return Collections.emptyList();
723 public List<Artifact> getArtifactDownloadInfos( String groupId, String artifactId, String version,
724 String repositoryId )
725 throws ArchivaRestServiceException
727 List<String> selectedRepos = getSelectedRepos( repositoryId );
729 List<Artifact> artifactDownloadInfos = new ArrayList<>();
731 try (RepositorySession session = repositorySessionFactory.createSession())
733 MetadataResolver metadataResolver = session.getResolver();
734 for ( String repoId : selectedRepos )
736 List<ArtifactMetadata> artifacts = new ArrayList<>(
737 metadataResolver.resolveArtifacts( session, repoId, groupId, artifactId, version ) );
738 Collections.sort( artifacts, ArtifactMetadataVersionComparator.INSTANCE );
739 if ( artifacts != null && !artifacts.isEmpty() )
741 return buildArtifacts( artifacts, repoId );
745 catch ( MetadataResolutionException e )
747 log.error( e.getMessage(), e );
748 throw new ArchivaRestServiceException( e.getMessage(),
749 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
752 return artifactDownloadInfos;
756 public ArtifactContent getArtifactContentText( String groupId, String artifactId, String version, String classifier,
757 String type, String path, String repositoryId )
758 throws ArchivaRestServiceException
760 List<String> selectedRepos = getSelectedRepos( repositoryId );
763 for ( String repoId : selectedRepos )
766 ManagedRepositoryContent managedRepositoryContent =
767 repositoryContentFactory.getManagedRepositoryContent( repoId );
768 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, classifier,
769 StringUtils.isEmpty( type ) ? "jar" : type,
771 Path file = managedRepositoryContent.toFile( archivaArtifact );
772 if ( !Files.exists(file) )
774 log.debug( "file: {} not exists for repository: {} try next repository", file, repoId );
777 if ( StringUtils.isNotBlank( path ) )
779 // zip entry of the path -> path must a real file entry of the archive
780 JarFile jarFile = new JarFile( file.toFile() );
781 ZipEntry zipEntry = jarFile.getEntry( path );
782 try (InputStream inputStream = jarFile.getInputStream( zipEntry ))
784 return new ArtifactContent( IOUtils.toString( inputStream, ARTIFACT_CONTENT_ENCODING ), repoId );
788 closeQuietly( jarFile );
791 return new ArtifactContent( new String(Files.readAllBytes( file ), ARTIFACT_CONTENT_ENCODING), repoId );
794 catch ( IOException e )
796 log.error( e.getMessage(), e );
797 throw new ArchivaRestServiceException( e.getMessage(),
798 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
800 catch ( RepositoryNotFoundException e )
802 log.error( e.getMessage(), e );
803 throw new ArchivaRestServiceException( e.getMessage(),
804 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
806 catch ( RepositoryException e )
808 log.error( e.getMessage(), e );
809 throw new ArchivaRestServiceException( e.getMessage(),
810 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
812 log.debug( "artifact: {}:{}:{}:{}:{} not found", groupId, artifactId, version, classifier, type );
814 return new ArtifactContent();
818 public Boolean artifactAvailable( String groupId, String artifactId, String version, String classifier,
819 String repositoryId )
820 throws ArchivaRestServiceException
822 List<String> selectedRepos = getSelectedRepos( repositoryId );
824 boolean snapshot = VersionUtil.isSnapshot( version );
828 for ( String repoId : selectedRepos )
831 ManagedRepository managedRepository = managedRepositoryAdmin.getManagedRepository( repoId );
833 if ( ( snapshot && !managedRepository.isSnapshots() ) || ( !snapshot
834 && managedRepository.isSnapshots() ) )
838 ManagedRepositoryContent managedRepositoryContent =
839 repositoryContentFactory.getManagedRepositoryContent( repoId );
840 // FIXME default to jar which can be wrong for war zip etc....
841 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version,
842 StringUtils.isEmpty( classifier )
844 : classifier, "jar", repoId );
845 Path file = managedRepositoryContent.toFile( archivaArtifact );
847 if ( file != null && Files.exists(file) )
852 // in case of SNAPSHOT we can have timestamped version locally !
853 if ( StringUtils.endsWith( version, VersionUtil.SNAPSHOT ) )
855 Path metadataFile = file.getParent().resolve(MetadataTools.MAVEN_METADATA );
856 if ( Files.exists(metadataFile) )
860 ArchivaRepositoryMetadata archivaRepositoryMetadata =
861 MavenMetadataReader.read( metadataFile );
862 int buildNumber = archivaRepositoryMetadata.getSnapshotVersion().getBuildNumber();
863 String timeStamp = archivaRepositoryMetadata.getSnapshotVersion().getTimestamp();
864 // rebuild file name with timestamped version and build number
865 String timeStampFileName = new StringBuilder( artifactId ).append( '-' ) //
866 .append( StringUtils.remove( version, "-" + VersionUtil.SNAPSHOT ) ) //
867 .append( '-' ).append( timeStamp ) //
868 .append( '-' ).append( Integer.toString( buildNumber ) ) //
869 .append( ( StringUtils.isEmpty( classifier ) ? "" : "-" + classifier ) ) //
870 .append( ".jar" ).toString();
872 Path timeStampFile = file.getParent().resolve( timeStampFileName );
873 log.debug( "try to find timestamped snapshot version file: {}", timeStampFile.toAbsolutePath() );
874 if ( Files.exists(timeStampFile) )
879 catch ( XMLException e )
881 log.warn( "skip fail to find timestamped snapshot file: {}", e.getMessage() );
886 String path = managedRepositoryContent.toPath( archivaArtifact );
888 file = connectors.fetchFromProxies( managedRepositoryContent, path );
890 if ( file != null && Files.exists(file) )
893 String pomPath = StringUtils.substringBeforeLast( path, ".jar" ) + ".pom";
894 connectors.fetchFromProxies( managedRepositoryContent, pomPath );
899 catch ( RepositoryAdminException e )
901 log.error( e.getMessage(), e );
902 throw new ArchivaRestServiceException( e.getMessage(),
903 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
905 catch ( RepositoryException e )
907 log.error( e.getMessage(), e );
908 throw new ArchivaRestServiceException( e.getMessage(),
909 Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
916 public Boolean artifactAvailable( String groupId, String artifactId, String version, String repositoryId )
917 throws ArchivaRestServiceException
919 return artifactAvailable( groupId, artifactId, version, null, repositoryId );
923 public List<Artifact> getArtifacts( String repositoryId )
924 throws ArchivaRestServiceException
926 RepositorySession repositorySession = repositorySessionFactory.createSession();
929 List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifacts( repositoryId );
930 return buildArtifacts( artifactMetadatas, repositoryId );
932 catch ( MetadataRepositoryException e )
934 throw new ArchivaRestServiceException( e.getMessage(), e );
938 repositorySession.close();
943 public List<Artifact> getArtifactsByProjectVersionMetadata( String key, String value, String repositoryId )
944 throws ArchivaRestServiceException
946 RepositorySession repositorySession = repositorySessionFactory.createSession();
949 List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifactsByProjectVersionMetadata( key, value, repositoryId );
950 return buildArtifacts( artifactMetadatas, repositoryId );
952 catch ( MetadataRepositoryException e )
954 throw new ArchivaRestServiceException( e.getMessage(), e );
958 repositorySession.close();
963 public List<Artifact> getArtifactsByMetadata( String key, String value, String repositoryId )
964 throws ArchivaRestServiceException
966 RepositorySession repositorySession = repositorySessionFactory.createSession();
969 List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifactsByMetadata( key, value, repositoryId );
970 return buildArtifacts( artifactMetadatas, repositoryId );
972 catch ( MetadataRepositoryException e )
974 throw new ArchivaRestServiceException( e.getMessage(), e );
978 repositorySession.close();
983 public List<Artifact> getArtifactsByProperty( String key, String value, String repositoryId )
984 throws ArchivaRestServiceException
986 RepositorySession repositorySession = repositorySessionFactory.createSession();
989 List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifactsByProperty( key, value, repositoryId );
990 return buildArtifacts( artifactMetadatas, repositoryId );
992 catch ( MetadataRepositoryException e )
994 throw new ArchivaRestServiceException( e.getMessage(), e );
998 repositorySession.close();
1003 public Boolean importMetadata( MetadataAddRequest metadataAddRequest, String repositoryId )
1004 throws ArchivaRestServiceException
1006 boolean result = true;
1007 for ( Map.Entry<String, String> metadata : metadataAddRequest.getMetadatas().entrySet() )
1009 result = addMetadata( metadataAddRequest.getGroupId(), metadataAddRequest.getArtifactId(),
1010 metadataAddRequest.getVersion(), metadata.getKey(), metadata.getValue(),
1021 public List<Artifact> searchArtifacts( String text, String repositoryId, Boolean exact )
1022 throws ArchivaRestServiceException
1024 RepositorySession repositorySession = repositorySessionFactory.createSession();
1027 List<ArtifactMetadata> artifactMetadatas =
1028 repositorySession.getRepository().searchArtifacts( text, repositoryId, exact == null ? false : exact );
1029 return buildArtifacts( artifactMetadatas, repositoryId );
1031 catch ( MetadataRepositoryException e )
1033 throw new ArchivaRestServiceException( e.getMessage(), e );
1037 repositorySession.close();
1042 public List<Artifact> searchArtifacts( String key, String text, String repositoryId, Boolean exact )
1043 throws ArchivaRestServiceException
1045 RepositorySession repositorySession = repositorySessionFactory.createSession();
1048 List<ArtifactMetadata> artifactMetadatas =
1049 repositorySession.getRepository().searchArtifacts( key, text, repositoryId, exact == null ? false : exact );
1050 return buildArtifacts( artifactMetadatas, repositoryId );
1052 catch ( MetadataRepositoryException e )
1054 throw new ArchivaRestServiceException( e.getMessage(), e );
1058 repositorySession.close();
1062 //---------------------------
1064 //---------------------------
1066 private void closeQuietly( JarFile jarFile )
1068 if ( jarFile != null )
1074 catch ( IOException e )
1076 log.warn( "ignore error closing jarFile {}", jarFile.getName() );
1081 protected List<ArtifactContentEntry> readFileEntries(final Path file, final String filterPath, final String repoId )
1084 String cleanedfilterPath = filterPath==null ? "" : (StringUtils.startsWith(filterPath, "/") ?
1085 StringUtils.substringAfter(filterPath, "/") : filterPath);
1086 Map<String, ArtifactContentEntry> artifactContentEntryMap = new HashMap<>();
1087 int filterDepth = StringUtils.countMatches( cleanedfilterPath, "/" );
1088 if (!StringUtils.endsWith(cleanedfilterPath,"/") && !StringUtils.isEmpty(cleanedfilterPath)) {
1091 JarFile jarFile = new JarFile( file.toFile() );
1094 Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
1095 while ( jarEntryEnumeration.hasMoreElements() )
1097 JarEntry currentEntry = jarEntryEnumeration.nextElement();
1098 String cleanedEntryName = StringUtils.endsWith( currentEntry.getName(), "/" ) ? //
1099 StringUtils.substringBeforeLast( currentEntry.getName(), "/" ) : currentEntry.getName();
1100 String entryRootPath = getRootPath( cleanedEntryName );
1101 int depth = StringUtils.countMatches( cleanedEntryName, "/" );
1102 if ( StringUtils.isEmpty( cleanedfilterPath ) //
1103 && !artifactContentEntryMap.containsKey( entryRootPath ) //
1104 && depth == filterDepth )
1107 artifactContentEntryMap.put( entryRootPath,
1108 new ArtifactContentEntry( entryRootPath, !currentEntry.isDirectory(),
1113 if ( StringUtils.startsWith( cleanedEntryName, cleanedfilterPath ) //
1114 && ( depth == filterDepth || ( !currentEntry.isDirectory() && depth == filterDepth ) ) )
1116 artifactContentEntryMap.put( cleanedEntryName, new ArtifactContentEntry( cleanedEntryName,
1117 !currentEntry.isDirectory(),
1123 if ( StringUtils.isNotEmpty( cleanedfilterPath ) )
1125 Map<String, ArtifactContentEntry> filteredArtifactContentEntryMap = new HashMap<>();
1127 for ( Map.Entry<String, ArtifactContentEntry> entry : artifactContentEntryMap.entrySet() )
1129 filteredArtifactContentEntryMap.put( entry.getKey(), entry.getValue() );
1132 List<ArtifactContentEntry> sorted = getSmallerDepthEntries( filteredArtifactContentEntryMap );
1133 if ( sorted == null )
1135 return Collections.emptyList();
1137 Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
1143 if ( jarFile != null )
1148 List<ArtifactContentEntry> sorted = new ArrayList<>( artifactContentEntryMap.values() );
1149 Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
1153 private List<ArtifactContentEntry> getSmallerDepthEntries( Map<String, ArtifactContentEntry> entries )
1155 int smallestDepth = Integer.MAX_VALUE;
1156 Map<Integer, List<ArtifactContentEntry>> perDepthList = new HashMap<>();
1157 for ( Map.Entry<String, ArtifactContentEntry> entry : entries.entrySet() )
1160 ArtifactContentEntry current = entry.getValue();
1162 if ( current.getDepth() < smallestDepth )
1164 smallestDepth = current.getDepth();
1167 List<ArtifactContentEntry> currentList = perDepthList.get( current.getDepth() );
1169 if ( currentList == null )
1171 currentList = new ArrayList<>();
1172 currentList.add( current );
1173 perDepthList.put( current.getDepth(), currentList );
1177 currentList.add( current );
1182 return perDepthList.get( smallestDepth );
1187 * @return org/apache -> org , org -> org
1189 private String getRootPath( String path )
1191 if ( StringUtils.contains( path, '/' ) )
1193 return StringUtils.substringBefore( path, "/" );
1198 private List<String> getSelectedRepos( String repositoryId )
1199 throws ArchivaRestServiceException
1202 List<String> selectedRepos = getObservableRepos();
1204 if ( CollectionUtils.isEmpty( selectedRepos ) )
1206 return Collections.emptyList();
1209 if ( StringUtils.isNotEmpty( repositoryId ) )
1211 // check user has karma on the repository
1212 if ( !selectedRepos.contains( repositoryId ) )
1214 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
1215 Response.Status.FORBIDDEN.getStatusCode(), null );
1217 selectedRepos = Collections.singletonList( repositoryId );
1219 return selectedRepos;
1223 private String collapseNamespaces( RepositorySession repositorySession, MetadataResolver metadataResolver,
1224 Collection<String> repoIds, String n )
1225 throws MetadataResolutionException
1227 Set<String> subNamespaces = new LinkedHashSet<String>();
1228 for ( String repoId : repoIds )
1230 subNamespaces.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, n ) );
1232 if ( subNamespaces.size() != 1 )
1234 log.debug( "{} is not collapsible as it has sub-namespaces: {}", n, subNamespaces );
1239 for ( String repoId : repoIds )
1241 Collection<String> projects = metadataResolver.resolveProjects( repositorySession, repoId, n );
1242 if ( projects != null && !projects.isEmpty() )
1244 log.debug( "{} is not collapsible as it has projects", n );
1248 return collapseNamespaces( repositorySession, metadataResolver, repoIds,
1249 n + "." + subNamespaces.iterator().next() );
1253 public Cache getVersionMetadataCache()
1255 return versionMetadataCache;
1258 public void setVersionMetadataCache( Cache versionMetadataCache )
1260 this.versionMetadataCache = versionMetadataCache;