]> source.dussan.org Git - archiva.git/blob
67397616feacadd984b9b898a13f5f3bbea70e2b
[archiva.git] /
1 package org.apache.archiva.rest.services;
2 /*
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  */
20
21 import org.apache.archiva.admin.model.beans.ManagedRepository;
22 import org.apache.archiva.common.utils.VersionComparator;
23 import org.apache.archiva.dependency.tree.maven2.DependencyTreeBuilder;
24 import org.apache.archiva.dependency.tree.maven2.Maven3DependencyTreeBuilder;
25 import org.apache.archiva.metadata.generic.GenericMetadataFacet;
26 import org.apache.archiva.metadata.model.ArtifactMetadata;
27 import org.apache.archiva.metadata.model.MetadataFacet;
28 import org.apache.archiva.metadata.model.ProjectVersionMetadata;
29 import org.apache.archiva.metadata.model.ProjectVersionReference;
30 import org.apache.archiva.metadata.repository.MetadataRepository;
31 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
32 import org.apache.archiva.metadata.repository.MetadataResolutionException;
33 import org.apache.archiva.metadata.repository.MetadataResolver;
34 import org.apache.archiva.metadata.repository.RepositorySession;
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.repository.ManagedRepositoryContent;
39 import org.apache.archiva.repository.RepositoryContentFactory;
40 import org.apache.archiva.repository.RepositoryException;
41 import org.apache.archiva.repository.RepositoryNotFoundException;
42 import org.apache.archiva.rest.api.model.Artifact;
43 import org.apache.archiva.rest.api.model.ArtifactContent;
44 import org.apache.archiva.rest.api.model.ArtifactContentEntry;
45 import org.apache.archiva.rest.api.model.BrowseResult;
46 import org.apache.archiva.rest.api.model.BrowseResultEntry;
47 import org.apache.archiva.rest.api.model.Entry;
48 import org.apache.archiva.rest.api.model.TreeEntry;
49 import org.apache.archiva.rest.api.model.VersionsList;
50 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
51 import org.apache.archiva.rest.api.services.BrowseService;
52 import org.apache.archiva.rest.services.utils.ArtifactContentEntryComparator;
53 import org.apache.archiva.rest.services.utils.ArtifactDownloadInfoBuilder;
54 import org.apache.archiva.rest.services.utils.TreeDependencyNodeVisitor;
55 import org.apache.archiva.security.ArchivaSecurityException;
56 import org.apache.commons.collections.CollectionUtils;
57 import org.apache.commons.io.FileUtils;
58 import org.apache.commons.io.IOUtils;
59 import org.apache.commons.lang.StringUtils;
60 import org.apache.maven.project.DependencyResolutionResult;
61 import org.apache.maven.shared.dependency.tree.DependencyTreeBuilderException;
62 import org.springframework.stereotype.Service;
63
64 import javax.inject.Inject;
65 import javax.ws.rs.core.Response;
66 import java.io.File;
67 import java.io.IOException;
68 import java.io.InputStream;
69 import java.util.ArrayList;
70 import java.util.Arrays;
71 import java.util.Collection;
72 import java.util.Collections;
73 import java.util.Enumeration;
74 import java.util.HashMap;
75 import java.util.LinkedHashSet;
76 import java.util.List;
77 import java.util.Map;
78 import java.util.Set;
79 import java.util.jar.JarEntry;
80 import java.util.jar.JarFile;
81 import java.util.zip.ZipEntry;
82
83 /**
84  * @author Olivier Lamy
85  * @since 1.4-M3
86  */
87 @Service( "browseService#rest" )
88 public class DefaultBrowseService
89     extends AbstractRestService
90     implements BrowseService
91 {
92
93     @Inject
94     private DependencyTreeBuilder dependencyTreeBuilder;
95
96     @Inject
97     private Maven3DependencyTreeBuilder maven3DependencyTreeBuilder;
98
99     @Inject
100     private RepositoryContentFactory repositoryContentFactory;
101
102     public BrowseResult getRootGroups( String repositoryId )
103         throws ArchivaRestServiceException
104     {
105         List<String> selectedRepos = getSelectedRepos( repositoryId );
106
107         Set<String> namespaces = new LinkedHashSet<String>();
108
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;
113         RepositorySession repositorySession = repositorySessionFactory.createSession();
114         try
115         {
116             MetadataResolver metadataResolver = repositorySession.getResolver();
117             namespacesToCollapse = new LinkedHashSet<String>();
118
119             for ( String repoId : selectedRepos )
120             {
121                 namespacesToCollapse.addAll( metadataResolver.resolveRootNamespaces( repositorySession, repoId ) );
122             }
123             for ( String n : namespacesToCollapse )
124             {
125                 // TODO: check performance of this
126                 namespaces.add( collapseNamespaces( repositorySession, metadataResolver, selectedRepos, n ) );
127             }
128         }
129         catch ( MetadataResolutionException e )
130         {
131             throw new ArchivaRestServiceException( e.getMessage(),
132                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
133         }
134         finally
135         {
136             repositorySession.close();
137         }
138
139         List<BrowseResultEntry> browseGroupResultEntries = new ArrayList<BrowseResultEntry>( namespaces.size() );
140         for ( String namespace : namespaces )
141         {
142             browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
143         }
144
145         Collections.sort( browseGroupResultEntries );
146         return new BrowseResult( browseGroupResultEntries );
147     }
148
149     public BrowseResult browseGroupId( String groupId, String repositoryId )
150         throws ArchivaRestServiceException
151     {
152         List<String> selectedRepos = getSelectedRepos( repositoryId );
153
154         Set<String> projects = new LinkedHashSet<String>();
155
156         RepositorySession repositorySession = repositorySessionFactory.createSession();
157         Set<String> namespaces;
158         try
159         {
160             MetadataResolver metadataResolver = repositorySession.getResolver();
161
162             Set<String> namespacesToCollapse = new LinkedHashSet<String>();
163             for ( String repoId : selectedRepos )
164             {
165                 namespacesToCollapse.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, groupId ) );
166
167                 projects.addAll( metadataResolver.resolveProjects( repositorySession, repoId, groupId ) );
168             }
169
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<String>();
174             for ( String n : namespacesToCollapse )
175             {
176                 // TODO: check performance of this
177                 namespaces.add(
178                     collapseNamespaces( repositorySession, metadataResolver, selectedRepos, groupId + "." + n ) );
179             }
180         }
181         catch ( MetadataResolutionException e )
182         {
183             throw new ArchivaRestServiceException( e.getMessage(),
184                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
185         }
186         finally
187         {
188             repositorySession.close();
189         }
190         List<BrowseResultEntry> browseGroupResultEntries =
191             new ArrayList<BrowseResultEntry>( namespaces.size() + projects.size() );
192         for ( String namespace : namespaces )
193         {
194             browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
195         }
196         for ( String project : projects )
197         {
198             browseGroupResultEntries.add( new BrowseResultEntry( groupId + '.' + project, true ) );
199         }
200         Collections.sort( browseGroupResultEntries );
201         return new BrowseResult( browseGroupResultEntries );
202
203     }
204
205     public VersionsList getVersionsList( String groupId, String artifactId, String repositoryId )
206         throws ArchivaRestServiceException
207     {
208         List<String> selectedRepos = getSelectedRepos( repositoryId );
209
210         try
211         {
212             Collection<String> versions = getVersions( selectedRepos, groupId, artifactId );
213             return new VersionsList( new ArrayList<String>( versions ) );
214         }
215         catch ( MetadataResolutionException e )
216         {
217             throw new ArchivaRestServiceException( e.getMessage(),
218                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
219         }
220
221     }
222
223     private Collection<String> getVersions( List<String> selectedRepos, String groupId, String artifactId )
224         throws MetadataResolutionException
225
226     {
227         RepositorySession repositorySession = repositorySessionFactory.createSession();
228         try
229         {
230             MetadataResolver metadataResolver = repositorySession.getResolver();
231
232             Set<String> versions = new LinkedHashSet<String>();
233
234             for ( String repoId : selectedRepos )
235             {
236                 Collection<String> projectVersions =
237                     metadataResolver.resolveProjectVersions( repositorySession, repoId, groupId, artifactId );
238                 versions.addAll( projectVersions );
239             }
240
241             List<String> sortedVersions = new ArrayList<String>( versions );
242
243             Collections.sort( sortedVersions, VersionComparator.getInstance() );
244
245             return sortedVersions;
246         }
247         finally
248         {
249             repositorySession.close();
250         }
251     }
252
253     public ProjectVersionMetadata getProjectMetadata( String groupId, String artifactId, String version,
254                                                       String repositoryId )
255         throws ArchivaRestServiceException
256     {
257         List<String> selectedRepos = getSelectedRepos( repositoryId );
258
259         RepositorySession repositorySession = null;
260         try
261         {
262             repositorySession = repositorySessionFactory.createSession();
263
264             MetadataResolver metadataResolver = repositorySession.getResolver();
265
266             ProjectVersionMetadata versionMetadata = null;
267             for ( String repoId : selectedRepos )
268             {
269                 if ( versionMetadata == null || versionMetadata.isIncomplete() )
270                 {
271                     try
272                     {
273                         versionMetadata =
274                             metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
275                                                                     version );
276                     }
277                     catch ( MetadataResolutionException e )
278                     {
279                         log.error(
280                             "Skipping invalid metadata while compiling shared model for " + groupId + ":" + artifactId
281                                 + " in repo " + repoId + ": " + e.getMessage() );
282                     }
283                 }
284             }
285
286             return versionMetadata;
287         }
288         finally
289         {
290             if ( repositorySession != null )
291             {
292                 repositorySession.close();
293             }
294         }
295
296     }
297
298     public ProjectVersionMetadata getProjectVersionMetadata( String groupId, String artifactId, String repositoryId )
299         throws ArchivaRestServiceException
300     {
301
302         List<String> selectedRepos = getSelectedRepos( repositoryId );
303
304         RepositorySession repositorySession = null;
305         try
306         {
307
308             Collection<String> projectVersions = getVersions( selectedRepos, groupId, artifactId );
309
310             repositorySession = repositorySessionFactory.createSession();
311
312             MetadataResolver metadataResolver = repositorySession.getResolver();
313
314             ProjectVersionMetadata sharedModel = new ProjectVersionMetadata();
315
316             MavenProjectFacet mavenFacet = new MavenProjectFacet();
317             mavenFacet.setGroupId( groupId );
318             mavenFacet.setArtifactId( artifactId );
319             sharedModel.addFacet( mavenFacet );
320
321             boolean isFirstVersion = true;
322
323             for ( String version : projectVersions )
324             {
325                 ProjectVersionMetadata versionMetadata = null;
326                 for ( String repoId : selectedRepos )
327                 {
328                     if ( versionMetadata == null || versionMetadata.isIncomplete() )
329                     {
330                         try
331                         {
332                             versionMetadata =
333                                 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
334                                                                         version );
335                         }
336                         catch ( MetadataResolutionException e )
337                         {
338                             log.error( "Skipping invalid metadata while compiling shared model for " + groupId + ":"
339                                            + artifactId + " in repo " + repoId + ": " + e.getMessage() );
340                         }
341                     }
342                 }
343
344                 if ( versionMetadata == null )
345                 {
346                     continue;
347                 }
348
349                 if ( isFirstVersion )
350                 {
351                     sharedModel = versionMetadata;
352                     sharedModel.setId( null );
353                 }
354                 else
355                 {
356                     MavenProjectFacet versionMetadataMavenFacet =
357                         (MavenProjectFacet) versionMetadata.getFacet( MavenProjectFacet.FACET_ID );
358                     if ( versionMetadataMavenFacet != null )
359                     {
360                         if ( mavenFacet.getPackaging() != null && !StringUtils.equalsIgnoreCase(
361                             mavenFacet.getPackaging(), versionMetadataMavenFacet.getPackaging() ) )
362                         {
363                             mavenFacet.setPackaging( null );
364                         }
365                     }
366
367                     if ( StringUtils.isEmpty( sharedModel.getName() ) && !StringUtils.isEmpty(
368                         versionMetadata.getName() ) )
369                     {
370                         sharedModel.setName( versionMetadata.getName() );
371                     }
372
373                     if ( sharedModel.getDescription() != null && !StringUtils.equalsIgnoreCase(
374                         sharedModel.getDescription(), versionMetadata.getDescription() ) )
375                     {
376                         sharedModel.setDescription( StringUtils.isNotEmpty( versionMetadata.getDescription() )
377                                                         ? versionMetadata.getDescription()
378                                                         : "" );
379                     }
380
381                     if ( sharedModel.getIssueManagement() != null && versionMetadata.getIssueManagement() != null
382                         && !StringUtils.equalsIgnoreCase( sharedModel.getIssueManagement().getUrl(),
383                                                           versionMetadata.getIssueManagement().getUrl() ) )
384                     {
385                         sharedModel.setIssueManagement( versionMetadata.getIssueManagement() );
386                     }
387
388                     if ( sharedModel.getCiManagement() != null && versionMetadata.getCiManagement() != null
389                         && !StringUtils.equalsIgnoreCase( sharedModel.getCiManagement().getUrl(),
390                                                           versionMetadata.getCiManagement().getUrl() ) )
391                     {
392                         sharedModel.setCiManagement( versionMetadata.getCiManagement() );
393                     }
394
395                     if ( sharedModel.getOrganization() != null && versionMetadata.getOrganization() != null
396                         && !StringUtils.equalsIgnoreCase( sharedModel.getOrganization().getName(),
397                                                           versionMetadata.getOrganization().getName() ) )
398                     {
399                         sharedModel.setOrganization( versionMetadata.getOrganization() );
400                     }
401
402                     if ( sharedModel.getUrl() != null && !StringUtils.equalsIgnoreCase( sharedModel.getUrl(),
403                                                                                         versionMetadata.getUrl() ) )
404                     {
405                         sharedModel.setUrl( versionMetadata.getUrl() );
406                     }
407                 }
408
409                 isFirstVersion = false;
410             }
411             return sharedModel;
412         }
413         catch ( MetadataResolutionException e )
414         {
415             throw new ArchivaRestServiceException( e.getMessage(),
416                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
417         }
418         finally
419         {
420             if ( repositorySession != null )
421             {
422                 repositorySession.close();
423             }
424         }
425     }
426
427     public List<TreeEntry> getTreeEntries( String groupId, String artifactId, String version, String repositoryId )
428         throws ArchivaRestServiceException
429     {
430         List<String> selectedRepos = getSelectedRepos( repositoryId );
431
432         List<TreeEntry> treeEntries = new ArrayList<TreeEntry>();
433         TreeDependencyNodeVisitor treeDependencyNodeVisitor = new TreeDependencyNodeVisitor( treeEntries );
434         /*
435         try
436         {
437             dependencyTreeBuilder.buildDependencyTree( selectedRepos, groupId, artifactId, version,
438                                                        treeDependencyNodeVisitor );
439         }
440         catch ( DependencyTreeBuilderException e )
441         {
442             throw new ArchivaRestServiceException( e.getMessage(),
443                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
444         }
445         */
446         try
447         {
448             DependencyResolutionResult result =
449                 maven3DependencyTreeBuilder.buildDependencyTree( selectedRepos, groupId, artifactId, version, treeDependencyNodeVisitor );
450             log.debug( "result: {}", result );
451         }
452         catch ( Exception e )
453         {
454             log.error( e.getMessage(), e );
455         }
456
457         return treeEntries;
458     }
459
460     public List<ManagedRepository> getUserRepositories()
461         throws ArchivaRestServiceException
462     {
463         try
464         {
465             return userRepositories.getAccessibleRepositories( getPrincipal() );
466         }
467         catch ( ArchivaSecurityException e )
468         {
469             throw new ArchivaRestServiceException( "repositories.read.observable.error",
470                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
471         }
472     }
473
474     public List<Artifact> getDependees( String groupId, String artifactId, String version, String repositoryId )
475         throws ArchivaRestServiceException
476     {
477         List<ProjectVersionReference> references = new ArrayList<ProjectVersionReference>();
478         // TODO: what if we get duplicates across repositories?
479         RepositorySession repositorySession = repositorySessionFactory.createSession();
480         try
481         {
482             MetadataResolver metadataResolver = repositorySession.getResolver();
483             for ( String repoId : getObservableRepos() )
484             {
485                 // TODO: what about if we want to see this irrespective of version?
486                 references.addAll(
487                     metadataResolver.resolveProjectReferences( repositorySession, repoId, groupId, artifactId,
488                                                                version ) );
489             }
490         }
491         catch ( MetadataResolutionException e )
492         {
493             throw new ArchivaRestServiceException( e.getMessage(),
494                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
495         }
496         finally
497         {
498             repositorySession.close();
499         }
500
501         List<Artifact> artifacts = new ArrayList<Artifact>( references.size() );
502
503         for ( ProjectVersionReference projectVersionReference : references )
504         {
505             artifacts.add( new Artifact( projectVersionReference.getNamespace(), projectVersionReference.getProjectId(),
506                                          projectVersionReference.getProjectVersion() ) );
507         }
508         return artifacts;
509     }
510
511     public List<Entry> getMetadatas( String groupId, String artifactId, String version, String repositoryId )
512         throws ArchivaRestServiceException
513     {
514         ProjectVersionMetadata projectVersionMetadata =
515             getProjectMetadata( groupId, artifactId, version, repositoryId );
516         if ( projectVersionMetadata == null )
517         {
518             return Collections.emptyList();
519         }
520         MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
521
522         if ( metadataFacet == null )
523         {
524             return Collections.emptyList();
525         }
526         Map<String, String> map = metadataFacet.toProperties();
527         List<Entry> entries = new ArrayList<Entry>( map.size() );
528
529         for ( Map.Entry<String, String> entry : map.entrySet() )
530         {
531             entries.add( new Entry( entry.getKey(), entry.getValue() ) );
532         }
533
534         return entries;
535     }
536
537     public Boolean addMetadata( String groupId, String artifactId, String version, String key, String value,
538                                 String repositoryId )
539         throws ArchivaRestServiceException
540     {
541         ProjectVersionMetadata projectVersionMetadata =
542             getProjectMetadata( groupId, artifactId, version, repositoryId );
543
544         if ( projectVersionMetadata == null )
545         {
546             return Boolean.FALSE;
547         }
548
549         Map<String, String> properties = new HashMap<String, String>();
550
551         MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
552
553         if ( metadataFacet != null && metadataFacet.toProperties() != null )
554         {
555             properties.putAll( metadataFacet.toProperties() );
556         }
557         else
558         {
559             metadataFacet = new GenericMetadataFacet();
560         }
561
562         properties.put( key, value );
563
564         metadataFacet.fromProperties( properties );
565
566         projectVersionMetadata.addFacet( metadataFacet );
567
568         RepositorySession repositorySession = repositorySessionFactory.createSession();
569
570         try
571         {
572             MetadataRepository metadataRepository = repositorySession.getRepository();
573
574             metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
575
576             repositorySession.save();
577         }
578         catch ( MetadataRepositoryException e )
579         {
580             log.error( e.getMessage(), e );
581             throw new ArchivaRestServiceException( e.getMessage(),
582                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
583         }
584         finally
585         {
586             repositorySession.close();
587         }
588         return Boolean.TRUE;
589     }
590
591     public Boolean deleteMetadata( String groupId, String artifactId, String version, String key, String repositoryId )
592         throws ArchivaRestServiceException
593     {
594         ProjectVersionMetadata projectVersionMetadata =
595             getProjectMetadata( groupId, artifactId, version, repositoryId );
596
597         if ( projectVersionMetadata == null )
598         {
599             return Boolean.FALSE;
600         }
601
602         GenericMetadataFacet metadataFacet =
603             (GenericMetadataFacet) projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
604
605         if ( metadataFacet != null && metadataFacet.toProperties() != null )
606         {
607             Map<String, String> properties = metadataFacet.toProperties();
608             properties.remove( key );
609             metadataFacet.setAdditionalProperties( properties );
610         }
611         else
612         {
613             return Boolean.TRUE;
614         }
615
616         RepositorySession repositorySession = repositorySessionFactory.createSession();
617
618         try
619         {
620             MetadataRepository metadataRepository = repositorySession.getRepository();
621
622             metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
623
624             repositorySession.save();
625         }
626         catch ( MetadataRepositoryException e )
627         {
628             log.error( e.getMessage(), e );
629             throw new ArchivaRestServiceException( e.getMessage(),
630                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
631         }
632         finally
633         {
634             repositorySession.close();
635         }
636         return Boolean.TRUE;
637     }
638
639     public List<ArtifactContentEntry> getArtifactContentEntries( String groupId, String artifactId, String version,
640                                                                  String classifier, String type, String path,
641                                                                  String repositoryId )
642         throws ArchivaRestServiceException
643     {
644         List<String> selectedRepos = getSelectedRepos( repositoryId );
645         try
646         {
647             for ( String repoId : selectedRepos )
648             {
649
650                 ManagedRepositoryContent managedRepositoryContent =
651                     repositoryContentFactory.getManagedRepositoryContent( repoId );
652                 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, classifier,
653                                                                        StringUtils.isEmpty( type ) ? "jar" : type,
654                                                                        repoId );
655                 File file = managedRepositoryContent.toFile( archivaArtifact );
656                 if ( file.exists() )
657                 {
658                     return readFileEntries( file, path, repoId );
659                 }
660             }
661         }
662         catch ( IOException e )
663         {
664             log.error( e.getMessage(), e );
665             throw new ArchivaRestServiceException( e.getMessage(),
666                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
667         }
668         catch ( RepositoryNotFoundException e )
669         {
670             log.error( e.getMessage(), e );
671             throw new ArchivaRestServiceException( e.getMessage(),
672                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
673         }
674         catch ( RepositoryException e )
675         {
676             log.error( e.getMessage(), e );
677             throw new ArchivaRestServiceException( e.getMessage(),
678                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
679         }
680         return Collections.emptyList();
681     }
682
683     public List<Artifact> getArtifactDownloadInfos( String groupId, String artifactId, String version,
684                                                     String repositoryId )
685         throws ArchivaRestServiceException
686     {
687         List<String> selectedRepos = getSelectedRepos( repositoryId );
688
689         List<Artifact> artifactDownloadInfos = new ArrayList<Artifact>();
690
691         RepositorySession session = repositorySessionFactory.createSession();
692
693         MetadataResolver metadataResolver = session.getResolver();
694
695         try
696         {
697             for ( String repoId : selectedRepos )
698             {
699                 List<ArtifactMetadata> artifacts = new ArrayList<ArtifactMetadata>(
700                     metadataResolver.resolveArtifacts( session, repoId, groupId, artifactId, version ) );
701                 Collections.sort( artifacts, ArtifactMetadataVersionComparator.INSTANCE );
702
703                 for ( ArtifactMetadata artifact : artifacts )
704                 {
705
706                     ArtifactDownloadInfoBuilder builder =
707                         new ArtifactDownloadInfoBuilder().forArtifactMetadata( artifact ).withManagedRepositoryContent(
708                             repositoryContentFactory.getManagedRepositoryContent( repoId ) );
709                     Artifact art = builder.build();
710
711                     art.setUrl( getArtifactUrl( art ) );
712                     artifactDownloadInfos.add( art );
713                 }
714
715             }
716         }
717         catch ( RepositoryException e )
718         {
719             log.error( e.getMessage(), e );
720             throw new ArchivaRestServiceException( e.getMessage(),
721                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
722         }
723         catch ( MetadataResolutionException e )
724         {
725             log.error( e.getMessage(), e );
726             throw new ArchivaRestServiceException( e.getMessage(),
727                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
728         }
729         finally
730         {
731             if ( session != null )
732             {
733                 session.close();
734             }
735         }
736
737         return artifactDownloadInfos;
738     }
739
740     public ArtifactContent getArtifactContentText( String groupId, String artifactId, String version, String classifier,
741                                                    String type, String path, String repositoryId )
742         throws ArchivaRestServiceException
743     {
744         List<String> selectedRepos = getSelectedRepos( repositoryId );
745         try
746         {
747             for ( String repoId : selectedRepos )
748             {
749
750                 ManagedRepositoryContent managedRepositoryContent =
751                     repositoryContentFactory.getManagedRepositoryContent( repoId );
752                 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, classifier,
753                                                                        StringUtils.isEmpty( type ) ? "jar" : type,
754                                                                        repoId );
755                 File file = managedRepositoryContent.toFile( archivaArtifact );
756                 if ( !file.exists() )
757                 {
758                     log.debug( "file: {} not exists for repository: {} try next repository", file, repoId );
759                     continue;
760                 }
761                 if ( StringUtils.isNotBlank( path ) )
762                 {
763                     // zip entry of the path -> path must a real file entry of the archive
764                     JarFile jarFile = new JarFile( file );
765                     ZipEntry zipEntry = jarFile.getEntry( path );
766                     InputStream inputStream = jarFile.getInputStream( zipEntry );
767                     try
768                     {
769                         return new ArtifactContent( IOUtils.toString( inputStream ), repoId );
770                     }
771                     finally
772                     {
773                         closeQuietly( jarFile );
774                         IOUtils.closeQuietly( inputStream );
775                     }
776                 }
777                 return new ArtifactContent( FileUtils.readFileToString( file ), repoId );
778             }
779         }
780         catch ( IOException e )
781         {
782             log.error( e.getMessage(), e );
783             throw new ArchivaRestServiceException( e.getMessage(),
784                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
785         }
786         catch ( RepositoryNotFoundException e )
787         {
788             log.error( e.getMessage(), e );
789             throw new ArchivaRestServiceException( e.getMessage(),
790                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
791         }
792         catch ( RepositoryException e )
793         {
794             log.error( e.getMessage(), e );
795             throw new ArchivaRestServiceException( e.getMessage(),
796                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
797         }
798         log.debug( "artifact: {}:{}:{}:{}:{} not found",
799                    Arrays.asList( groupId, artifactId, version, classifier, type ).toArray( new String[5] ) );
800         // 404 ?
801         return new ArtifactContent();
802     }
803
804     private void closeQuietly( JarFile jarFile )
805     {
806         if ( jarFile != null )
807         {
808             try
809             {
810                 jarFile.close();
811             }
812             catch ( IOException e )
813             {
814                 log.warn( "ignore error closing jarFile {}", jarFile.getName() );
815             }
816         }
817     }
818
819     //---------------------------
820     // internals
821     //---------------------------
822
823     protected List<ArtifactContentEntry> readFileEntries( File file, String filterPath, String repoId )
824         throws IOException
825     {
826         Map<String, ArtifactContentEntry> artifactContentEntryMap = new HashMap<String, ArtifactContentEntry>();
827         int filterDepth = StringUtils.countMatches( filterPath, "/" );
828         /*if ( filterDepth == 0 )
829         {
830             filterDepth = 1;
831         }*/
832         JarFile jarFile = new JarFile( file );
833         try
834         {
835             Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
836             while ( jarEntryEnumeration.hasMoreElements() )
837             {
838                 JarEntry currentEntry = jarEntryEnumeration.nextElement();
839                 String cleanedEntryName =
840                     StringUtils.endsWith( currentEntry.getName(), "/" ) ? StringUtils.substringBeforeLast(
841                         currentEntry.getName(), "/" ) : currentEntry.getName();
842                 String entryRootPath = getRootPath( cleanedEntryName );
843                 int depth = StringUtils.countMatches( cleanedEntryName, "/" );
844                 if ( StringUtils.isEmpty( filterPath ) && !artifactContentEntryMap.containsKey( entryRootPath )
845                     && depth == filterDepth )
846                 {
847
848                     artifactContentEntryMap.put( entryRootPath,
849                                                  new ArtifactContentEntry( entryRootPath, !currentEntry.isDirectory(),
850                                                                            depth, repoId ) );
851                 }
852                 else
853                 {
854                     if ( StringUtils.startsWith( cleanedEntryName, filterPath ) && ( depth == filterDepth || (
855                         !currentEntry.isDirectory() && depth == filterDepth ) ) )
856                     {
857                         artifactContentEntryMap.put( cleanedEntryName, new ArtifactContentEntry( cleanedEntryName,
858                                                                                                  !currentEntry.isDirectory(),
859                                                                                                  depth, repoId ) );
860                     }
861                 }
862             }
863
864             if ( StringUtils.isNotEmpty( filterPath ) )
865             {
866                 Map<String, ArtifactContentEntry> filteredArtifactContentEntryMap =
867                     new HashMap<String, ArtifactContentEntry>();
868
869                 for ( Map.Entry<String, ArtifactContentEntry> entry : artifactContentEntryMap.entrySet() )
870                 {
871                     filteredArtifactContentEntryMap.put( entry.getKey(), entry.getValue() );
872                 }
873
874                 List<ArtifactContentEntry> sorted = getSmallerDepthEntries( filteredArtifactContentEntryMap );
875                 if ( sorted == null )
876                 {
877                     return Collections.emptyList();
878                 }
879                 Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
880                 return sorted;
881             }
882         }
883         finally
884         {
885             if ( jarFile != null )
886             {
887                 jarFile.close();
888             }
889         }
890         List<ArtifactContentEntry> sorted = new ArrayList<ArtifactContentEntry>( artifactContentEntryMap.values() );
891         Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
892         return sorted;
893     }
894
895     private List<ArtifactContentEntry> getSmallerDepthEntries( Map<String, ArtifactContentEntry> entries )
896     {
897         int smallestDepth = Integer.MAX_VALUE;
898         Map<Integer, List<ArtifactContentEntry>> perDepthList = new HashMap<Integer, List<ArtifactContentEntry>>();
899         for ( Map.Entry<String, ArtifactContentEntry> entry : entries.entrySet() )
900         {
901
902             ArtifactContentEntry current = entry.getValue();
903
904             if ( current.getDepth() < smallestDepth )
905             {
906                 smallestDepth = current.getDepth();
907             }
908
909             List<ArtifactContentEntry> currentList = perDepthList.get( current.getDepth() );
910
911             if ( currentList == null )
912             {
913                 currentList = new ArrayList<ArtifactContentEntry>();
914                 currentList.add( current );
915                 perDepthList.put( current.getDepth(), currentList );
916             }
917             else
918             {
919                 currentList.add( current );
920             }
921
922         }
923
924         return perDepthList.get( smallestDepth );
925     }
926
927     /**
928      * @param path
929      * @return org/apache -> org , org -> org
930      */
931     private String getRootPath( String path )
932     {
933         if ( StringUtils.contains( path, '/' ) )
934         {
935             return StringUtils.substringBefore( path, "/" );
936         }
937         return path;
938     }
939
940     private List<String> getSelectedRepos( String repositoryId )
941         throws ArchivaRestServiceException
942     {
943
944         List<String> selectedRepos = getObservableRepos();
945
946         if ( CollectionUtils.isEmpty( selectedRepos ) )
947         {
948             // FIXME 403 ???
949             return Collections.emptyList();
950         }
951
952         if ( StringUtils.isNotEmpty( repositoryId ) )
953         {
954             // check user has karma on the repository
955             if ( !selectedRepos.contains( repositoryId ) )
956             {
957                 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
958                                                        Response.Status.FORBIDDEN.getStatusCode(), null );
959             }
960             selectedRepos = Collections.singletonList( repositoryId );
961         }
962         return selectedRepos;
963     }
964
965
966     private String collapseNamespaces( RepositorySession repositorySession, MetadataResolver metadataResolver,
967                                        Collection<String> repoIds, String n )
968         throws MetadataResolutionException
969     {
970         Set<String> subNamespaces = new LinkedHashSet<String>();
971         for ( String repoId : repoIds )
972         {
973             subNamespaces.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, n ) );
974         }
975         if ( subNamespaces.size() != 1 )
976         {
977             log.debug( "{} is not collapsible as it has sub-namespaces: {}", n, subNamespaces );
978             return n;
979         }
980         else
981         {
982             for ( String repoId : repoIds )
983             {
984                 Collection<String> projects = metadataResolver.resolveProjects( repositorySession, repoId, n );
985                 if ( projects != null && !projects.isEmpty() )
986                 {
987                     log.debug( "{} is not collapsible as it has projects", n );
988                     return n;
989                 }
990             }
991             return collapseNamespaces( repositorySession, metadataResolver, repoIds,
992                                        n + "." + subNamespaces.iterator().next() );
993         }
994     }
995 }