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