]> source.dussan.org Git - archiva.git/blob
061f8e87f9cf8a8eedfc0590bce330b470c7d0a9
[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.springframework.stereotype.Service;
61
62 import javax.inject.Inject;
63 import javax.ws.rs.core.Response;
64 import java.io.File;
65 import java.io.IOException;
66 import java.io.InputStream;
67 import java.util.ArrayList;
68 import java.util.Arrays;
69 import java.util.Collection;
70 import java.util.Collections;
71 import java.util.Enumeration;
72 import java.util.HashMap;
73 import java.util.LinkedHashSet;
74 import java.util.List;
75 import java.util.Map;
76 import java.util.Set;
77 import java.util.jar.JarEntry;
78 import java.util.jar.JarFile;
79 import java.util.zip.ZipEntry;
80
81 /**
82  * @author Olivier Lamy
83  * @since 1.4-M3
84  */
85 @Service( "browseService#rest" )
86 public class DefaultBrowseService
87     extends AbstractRestService
88     implements BrowseService
89 {
90
91     @Inject
92     private DependencyTreeBuilder dependencyTreeBuilder;
93
94     @Inject
95     private Maven3DependencyTreeBuilder maven3DependencyTreeBuilder;
96
97     @Inject
98     private RepositoryContentFactory repositoryContentFactory;
99
100     public BrowseResult getRootGroups( String repositoryId )
101         throws ArchivaRestServiceException
102     {
103         List<String> selectedRepos = getSelectedRepos( repositoryId );
104
105         Set<String> namespaces = new LinkedHashSet<String>();
106
107         // TODO: this logic should be optional, particularly remembering we want to keep this code simple
108         //       it is located here to avoid the content repository implementation needing to do too much for what
109         //       is essentially presentation code
110         Set<String> namespacesToCollapse;
111         RepositorySession repositorySession = repositorySessionFactory.createSession();
112         try
113         {
114             MetadataResolver metadataResolver = repositorySession.getResolver();
115             namespacesToCollapse = new LinkedHashSet<String>();
116
117             for ( String repoId : selectedRepos )
118             {
119                 namespacesToCollapse.addAll( metadataResolver.resolveRootNamespaces( repositorySession, repoId ) );
120             }
121             for ( String n : namespacesToCollapse )
122             {
123                 // TODO: check performance of this
124                 namespaces.add( collapseNamespaces( repositorySession, metadataResolver, selectedRepos, n ) );
125             }
126         }
127         catch ( MetadataResolutionException e )
128         {
129             throw new ArchivaRestServiceException( e.getMessage(),
130                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
131         }
132         finally
133         {
134             repositorySession.close();
135         }
136
137         List<BrowseResultEntry> browseGroupResultEntries = new ArrayList<BrowseResultEntry>( namespaces.size() );
138         for ( String namespace : namespaces )
139         {
140             browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
141         }
142
143         Collections.sort( browseGroupResultEntries );
144         return new BrowseResult( browseGroupResultEntries );
145     }
146
147     public BrowseResult browseGroupId( String groupId, String repositoryId )
148         throws ArchivaRestServiceException
149     {
150         List<String> selectedRepos = getSelectedRepos( repositoryId );
151
152         Set<String> projects = new LinkedHashSet<String>();
153
154         RepositorySession repositorySession = repositorySessionFactory.createSession();
155         Set<String> namespaces;
156         try
157         {
158             MetadataResolver metadataResolver = repositorySession.getResolver();
159
160             Set<String> namespacesToCollapse = new LinkedHashSet<String>();
161             for ( String repoId : selectedRepos )
162             {
163                 namespacesToCollapse.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, groupId ) );
164
165                 projects.addAll( metadataResolver.resolveProjects( repositorySession, repoId, groupId ) );
166             }
167
168             // TODO: this logic should be optional, particularly remembering we want to keep this code simple
169             // it is located here to avoid the content repository implementation needing to do too much for what
170             // is essentially presentation code
171             namespaces = new LinkedHashSet<String>();
172             for ( String n : namespacesToCollapse )
173             {
174                 // TODO: check performance of this
175                 namespaces.add(
176                     collapseNamespaces( repositorySession, metadataResolver, selectedRepos, groupId + "." + n ) );
177             }
178         }
179         catch ( MetadataResolutionException e )
180         {
181             throw new ArchivaRestServiceException( e.getMessage(),
182                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
183         }
184         finally
185         {
186             repositorySession.close();
187         }
188         List<BrowseResultEntry> browseGroupResultEntries =
189             new ArrayList<BrowseResultEntry>( namespaces.size() + projects.size() );
190         for ( String namespace : namespaces )
191         {
192             browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
193         }
194         for ( String project : projects )
195         {
196             browseGroupResultEntries.add( new BrowseResultEntry( groupId + '.' + project, true ) );
197         }
198         Collections.sort( browseGroupResultEntries );
199         return new BrowseResult( browseGroupResultEntries );
200
201     }
202
203     public VersionsList getVersionsList( String groupId, String artifactId, String repositoryId )
204         throws ArchivaRestServiceException
205     {
206         List<String> selectedRepos = getSelectedRepos( repositoryId );
207
208         try
209         {
210             Collection<String> versions = getVersions( selectedRepos, groupId, artifactId );
211             return new VersionsList( new ArrayList<String>( versions ) );
212         }
213         catch ( MetadataResolutionException e )
214         {
215             throw new ArchivaRestServiceException( e.getMessage(),
216                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
217         }
218
219     }
220
221     private Collection<String> getVersions( List<String> selectedRepos, String groupId, String artifactId )
222         throws MetadataResolutionException
223
224     {
225         RepositorySession repositorySession = repositorySessionFactory.createSession();
226         try
227         {
228             MetadataResolver metadataResolver = repositorySession.getResolver();
229
230             Set<String> versions = new LinkedHashSet<String>();
231
232             for ( String repoId : selectedRepos )
233             {
234                 Collection<String> projectVersions =
235                     metadataResolver.resolveProjectVersions( repositorySession, repoId, groupId, artifactId );
236                 versions.addAll( projectVersions );
237             }
238
239             List<String> sortedVersions = new ArrayList<String>( versions );
240
241             Collections.sort( sortedVersions, VersionComparator.getInstance() );
242
243             return sortedVersions;
244         }
245         finally
246         {
247             repositorySession.close();
248         }
249     }
250
251     public ProjectVersionMetadata getProjectMetadata( String groupId, String artifactId, String version,
252                                                       String repositoryId )
253         throws ArchivaRestServiceException
254     {
255         List<String> selectedRepos = getSelectedRepos( repositoryId );
256
257         RepositorySession repositorySession = null;
258         try
259         {
260             repositorySession = repositorySessionFactory.createSession();
261
262             MetadataResolver metadataResolver = repositorySession.getResolver();
263
264             ProjectVersionMetadata versionMetadata = null;
265             for ( String repoId : selectedRepos )
266             {
267                 if ( versionMetadata == null || versionMetadata.isIncomplete() )
268                 {
269                     try
270                     {
271                         versionMetadata =
272                             metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
273                                                                     version );
274                     }
275                     catch ( MetadataResolutionException e )
276                     {
277                         log.error(
278                             "Skipping invalid metadata while compiling shared model for " + groupId + ":" + artifactId
279                                 + " in repo " + repoId + ": " + e.getMessage() );
280                     }
281                 }
282             }
283
284             return versionMetadata;
285         }
286         finally
287         {
288             if ( repositorySession != null )
289             {
290                 repositorySession.close();
291             }
292         }
293
294     }
295
296     public ProjectVersionMetadata getProjectVersionMetadata( String groupId, String artifactId, String repositoryId )
297         throws ArchivaRestServiceException
298     {
299
300         List<String> selectedRepos = getSelectedRepos( repositoryId );
301
302         RepositorySession repositorySession = null;
303         try
304         {
305
306             Collection<String> projectVersions = getVersions( selectedRepos, groupId, artifactId );
307
308             repositorySession = repositorySessionFactory.createSession();
309
310             MetadataResolver metadataResolver = repositorySession.getResolver();
311
312             ProjectVersionMetadata sharedModel = new ProjectVersionMetadata();
313
314             MavenProjectFacet mavenFacet = new MavenProjectFacet();
315             mavenFacet.setGroupId( groupId );
316             mavenFacet.setArtifactId( artifactId );
317             sharedModel.addFacet( mavenFacet );
318
319             boolean isFirstVersion = true;
320
321             for ( String version : projectVersions )
322             {
323                 ProjectVersionMetadata versionMetadata = null;
324                 for ( String repoId : selectedRepos )
325                 {
326                     if ( versionMetadata == null || versionMetadata.isIncomplete() )
327                     {
328                         try
329                         {
330                             versionMetadata =
331                                 metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
332                                                                         version );
333                         }
334                         catch ( MetadataResolutionException e )
335                         {
336                             log.error( "Skipping invalid metadata while compiling shared model for " + groupId + ":"
337                                            + artifactId + " in repo " + repoId + ": " + e.getMessage() );
338                         }
339                     }
340                 }
341
342                 if ( versionMetadata == null )
343                 {
344                     continue;
345                 }
346
347                 if ( isFirstVersion )
348                 {
349                     sharedModel = versionMetadata;
350                     sharedModel.setId( null );
351                 }
352                 else
353                 {
354                     MavenProjectFacet versionMetadataMavenFacet =
355                         (MavenProjectFacet) versionMetadata.getFacet( MavenProjectFacet.FACET_ID );
356                     if ( versionMetadataMavenFacet != null )
357                     {
358                         if ( mavenFacet.getPackaging() != null && !StringUtils.equalsIgnoreCase(
359                             mavenFacet.getPackaging(), versionMetadataMavenFacet.getPackaging() ) )
360                         {
361                             mavenFacet.setPackaging( null );
362                         }
363                     }
364
365                     if ( StringUtils.isEmpty( sharedModel.getName() ) && !StringUtils.isEmpty(
366                         versionMetadata.getName() ) )
367                     {
368                         sharedModel.setName( versionMetadata.getName() );
369                     }
370
371                     if ( sharedModel.getDescription() != null && !StringUtils.equalsIgnoreCase(
372                         sharedModel.getDescription(), versionMetadata.getDescription() ) )
373                     {
374                         sharedModel.setDescription( StringUtils.isNotEmpty( versionMetadata.getDescription() )
375                                                         ? versionMetadata.getDescription()
376                                                         : "" );
377                     }
378
379                     if ( sharedModel.getIssueManagement() != null && versionMetadata.getIssueManagement() != null
380                         && !StringUtils.equalsIgnoreCase( sharedModel.getIssueManagement().getUrl(),
381                                                           versionMetadata.getIssueManagement().getUrl() ) )
382                     {
383                         sharedModel.setIssueManagement( versionMetadata.getIssueManagement() );
384                     }
385
386                     if ( sharedModel.getCiManagement() != null && versionMetadata.getCiManagement() != null
387                         && !StringUtils.equalsIgnoreCase( sharedModel.getCiManagement().getUrl(),
388                                                           versionMetadata.getCiManagement().getUrl() ) )
389                     {
390                         sharedModel.setCiManagement( versionMetadata.getCiManagement() );
391                     }
392
393                     if ( sharedModel.getOrganization() != null && versionMetadata.getOrganization() != null
394                         && !StringUtils.equalsIgnoreCase( sharedModel.getOrganization().getName(),
395                                                           versionMetadata.getOrganization().getName() ) )
396                     {
397                         sharedModel.setOrganization( versionMetadata.getOrganization() );
398                     }
399
400                     if ( sharedModel.getUrl() != null && !StringUtils.equalsIgnoreCase( sharedModel.getUrl(),
401                                                                                         versionMetadata.getUrl() ) )
402                     {
403                         sharedModel.setUrl( versionMetadata.getUrl() );
404                     }
405                 }
406
407                 isFirstVersion = false;
408             }
409             return sharedModel;
410         }
411         catch ( MetadataResolutionException e )
412         {
413             throw new ArchivaRestServiceException( e.getMessage(),
414                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
415         }
416         finally
417         {
418             if ( repositorySession != null )
419             {
420                 repositorySession.close();
421             }
422         }
423     }
424
425     public List<TreeEntry> getTreeEntries( String groupId, String artifactId, String version, String repositoryId )
426         throws ArchivaRestServiceException
427     {
428         List<String> selectedRepos = getSelectedRepos( repositoryId );
429
430         List<TreeEntry> treeEntries = new ArrayList<TreeEntry>();
431         TreeDependencyNodeVisitor treeDependencyNodeVisitor = new TreeDependencyNodeVisitor( treeEntries );
432         /*
433         try
434         {
435             dependencyTreeBuilder.buildDependencyTree( selectedRepos, groupId, artifactId, version,
436                                                        treeDependencyNodeVisitor );
437         }
438         catch ( DependencyTreeBuilderException e )
439         {
440             throw new ArchivaRestServiceException( e.getMessage(),
441                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
442         }
443         */
444         try
445         {
446
447             maven3DependencyTreeBuilder.buildDependencyTree( selectedRepos, groupId, artifactId, version,
448                                                              treeDependencyNodeVisitor );
449
450         }
451         catch ( Exception e )
452         {
453             log.error( e.getMessage(), e );
454         }
455
456         log.debug( "treeEntrie: {}", treeEntries );
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 }