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