]> source.dussan.org Git - archiva.git/blob
262bae592e1bc5af2b9ade21272750eb91977b22
[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.ArtifactContent;
43 import org.apache.archiva.rest.api.model.ArtifactContentEntry;
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, repoId );
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<Artifact> getArtifactDownloadInfos( String groupId, String artifactId, String version,
664                                                     String repositoryId )
665         throws ArchivaRestServiceException
666     {
667         List<String> selectedRepos = getSelectedRepos( repositoryId );
668
669         List<Artifact> artifactDownloadInfos = new ArrayList<Artifact>();
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                     Artifact art = builder.build();
690
691                     art.setUrl( getArtifactUrl( art, version ) );
692                     artifactDownloadInfos.add( art );
693                 }
694
695             }
696         }
697         catch ( RepositoryException e )
698         {
699             log.error( e.getMessage(), e );
700             throw new ArchivaRestServiceException( e.getMessage(),
701                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
702         }
703         catch ( MetadataResolutionException e )
704         {
705             log.error( e.getMessage(), e );
706             throw new ArchivaRestServiceException( e.getMessage(),
707                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
708         }
709         finally
710         {
711             if ( session != null )
712             {
713                 session.close();
714             }
715         }
716
717         return artifactDownloadInfos;
718     }
719
720     public ArtifactContent getArtifactContentText( String groupId, String artifactId, String version, String classifier,
721                                                    String type, String path, String repositoryId )
722         throws ArchivaRestServiceException
723     {
724         List<String> selectedRepos = getSelectedRepos( repositoryId );
725         try
726         {
727             for ( String repoId : selectedRepos )
728             {
729
730                 ManagedRepositoryContent managedRepositoryContent =
731                     repositoryContentFactory.getManagedRepositoryContent( repoId );
732                 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, classifier,
733                                                                        StringUtils.isEmpty( type ) ? "jar" : type,
734                                                                        repoId );
735                 File file = managedRepositoryContent.toFile( archivaArtifact );
736                 if ( !file.exists() )
737                 {
738                     log.debug( "file: {} not exists for repository: {} try next repository", file, repoId );
739                     continue;
740                 }
741                 if ( StringUtils.isNotBlank( path ) )
742                 {
743                     // zip entry of the path -> path must a real file entry of the archive
744                     JarFile jarFile = new JarFile( file );
745                     ZipEntry zipEntry = jarFile.getEntry( path );
746                     InputStream inputStream = jarFile.getInputStream( zipEntry );
747                     try
748                     {
749                         return new ArtifactContent( IOUtils.toString( inputStream ), repoId );
750                     }
751                     finally
752                     {
753                         IOUtils.closeQuietly( inputStream );
754                     }
755                 }
756                 return new ArtifactContent( FileUtils.readFileToString( file ), repoId );
757             }
758         }
759         catch ( IOException e )
760         {
761             log.error( e.getMessage(), e );
762             throw new ArchivaRestServiceException( e.getMessage(),
763                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
764         }
765         catch ( RepositoryNotFoundException e )
766         {
767             log.error( e.getMessage(), e );
768             throw new ArchivaRestServiceException( e.getMessage(),
769                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
770         }
771         catch ( RepositoryException e )
772         {
773             log.error( e.getMessage(), e );
774             throw new ArchivaRestServiceException( e.getMessage(),
775                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
776         }
777         log.debug( "artifact: {}:{}:{}:{}:{} not found",
778                    Arrays.asList( groupId, artifactId, version, classifier, type ).toArray( new String[5] ) );
779         // 404 ?
780         return new ArtifactContent();
781     }
782
783     //---------------------------
784     // internals
785     //---------------------------
786
787     protected List<ArtifactContentEntry> readFileEntries( File file, String filterPath, String repoId )
788         throws IOException
789     {
790         Map<String, ArtifactContentEntry> artifactContentEntryMap = new HashMap<String, ArtifactContentEntry>();
791         int filterDepth = StringUtils.countMatches( filterPath, "/" );
792         /*if ( filterDepth == 0 )
793         {
794             filterDepth = 1;
795         }*/
796         JarFile jarFile = new JarFile( file );
797         try
798         {
799             Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
800             while ( jarEntryEnumeration.hasMoreElements() )
801             {
802                 JarEntry currentEntry = jarEntryEnumeration.nextElement();
803                 String cleanedEntryName =
804                     StringUtils.endsWith( currentEntry.getName(), "/" ) ? StringUtils.substringBeforeLast(
805                         currentEntry.getName(), "/" ) : currentEntry.getName();
806                 String entryRootPath = getRootPath( cleanedEntryName );
807                 int depth = StringUtils.countMatches( cleanedEntryName, "/" );
808                 if ( StringUtils.isEmpty( filterPath ) && !artifactContentEntryMap.containsKey( entryRootPath )
809                     && depth == filterDepth )
810                 {
811
812                     artifactContentEntryMap.put( entryRootPath,
813                                                  new ArtifactContentEntry( entryRootPath, !currentEntry.isDirectory(),
814                                                                            depth, repoId ) );
815                 }
816                 else
817                 {
818                     if ( StringUtils.startsWith( cleanedEntryName, filterPath ) && ( depth == filterDepth || (
819                         !currentEntry.isDirectory() && depth == filterDepth ) ) )
820                     {
821                         artifactContentEntryMap.put( cleanedEntryName, new ArtifactContentEntry( cleanedEntryName,
822                                                                                                  !currentEntry.isDirectory(),
823                                                                                                  depth, repoId ) );
824                     }
825                 }
826             }
827
828             if ( StringUtils.isNotEmpty( filterPath ) )
829             {
830                 Map<String, ArtifactContentEntry> filteredArtifactContentEntryMap =
831                     new HashMap<String, ArtifactContentEntry>();
832
833                 for ( Map.Entry<String, ArtifactContentEntry> entry : artifactContentEntryMap.entrySet() )
834                 {
835                     filteredArtifactContentEntryMap.put( entry.getKey(), entry.getValue() );
836                 }
837
838                 List<ArtifactContentEntry> sorted = getSmallerDepthEntries( filteredArtifactContentEntryMap );
839                 if ( sorted == null )
840                 {
841                     return Collections.emptyList();
842                 }
843                 Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
844                 return sorted;
845             }
846         }
847         finally
848         {
849             if ( jarFile != null )
850             {
851                 jarFile.close();
852             }
853         }
854         List<ArtifactContentEntry> sorted = new ArrayList<ArtifactContentEntry>( artifactContentEntryMap.values() );
855         Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
856         return sorted;
857     }
858
859     private List<ArtifactContentEntry> getSmallerDepthEntries( Map<String, ArtifactContentEntry> entries )
860     {
861         int smallestDepth = Integer.MAX_VALUE;
862         Map<Integer, List<ArtifactContentEntry>> perDepthList = new HashMap<Integer, List<ArtifactContentEntry>>();
863         for ( Map.Entry<String, ArtifactContentEntry> entry : entries.entrySet() )
864         {
865
866             ArtifactContentEntry current = entry.getValue();
867
868             if ( current.getDepth() < smallestDepth )
869             {
870                 smallestDepth = current.getDepth();
871             }
872
873             List<ArtifactContentEntry> currentList = perDepthList.get( current.getDepth() );
874
875             if ( currentList == null )
876             {
877                 currentList = new ArrayList<ArtifactContentEntry>();
878                 currentList.add( current );
879                 perDepthList.put( current.getDepth(), currentList );
880             }
881             else
882             {
883                 currentList.add( current );
884             }
885
886         }
887
888         return perDepthList.get( smallestDepth );
889     }
890
891     /**
892      * @param path
893      * @return org/apache -> org , org -> org
894      */
895     private String getRootPath( String path )
896     {
897         if ( StringUtils.contains( path, '/' ) )
898         {
899             return StringUtils.substringBefore( path, "/" );
900         }
901         return path;
902     }
903
904     private List<String> getSelectedRepos( String repositoryId )
905         throws ArchivaRestServiceException
906     {
907
908         List<String> selectedRepos = getObservableRepos();
909
910         if ( CollectionUtils.isEmpty( selectedRepos ) )
911         {
912             // FIXME 403 ???
913             return Collections.emptyList();
914         }
915
916         if ( StringUtils.isNotEmpty( repositoryId ) )
917         {
918             // check user has karma on the repository
919             if ( !selectedRepos.contains( repositoryId ) )
920             {
921                 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
922                                                        Response.Status.FORBIDDEN.getStatusCode(), null );
923             }
924             selectedRepos = Collections.singletonList( repositoryId );
925         }
926         return selectedRepos;
927     }
928
929
930     private String collapseNamespaces( RepositorySession repositorySession, MetadataResolver metadataResolver,
931                                        Collection<String> repoIds, String n )
932         throws MetadataResolutionException
933     {
934         Set<String> subNamespaces = new LinkedHashSet<String>();
935         for ( String repoId : repoIds )
936         {
937             subNamespaces.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, n ) );
938         }
939         if ( subNamespaces.size() != 1 )
940         {
941             log.debug( "{} is not collapsible as it has sub-namespaces: {}", n, subNamespaces );
942             return n;
943         }
944         else
945         {
946             for ( String repoId : repoIds )
947             {
948                 Collection<String> projects = metadataResolver.resolveProjects( repositorySession, repoId, n );
949                 if ( projects != null && !projects.isEmpty() )
950                 {
951                     log.debug( "{} is not collapsible as it has projects", n );
952                     return n;
953                 }
954             }
955             return collapseNamespaces( repositorySession, metadataResolver, repoIds,
956                                        n + "." + subNamespaces.iterator().next() );
957         }
958     }
959 }