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