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