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