]> source.dussan.org Git - archiva.git/blob
9488261b45e525c03579983d96b289dae9065efb
[archiva.git] /
1 package org.apache.archiva.rest.services;
2 /*
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  */
20
21 import org.apache.archiva.admin.model.beans.ManagedRepository;
22 import org.apache.archiva.common.utils.VersionComparator;
23 import org.apache.archiva.common.utils.VersionUtil;
24 import org.apache.archiva.dependency.tree.maven2.DependencyTreeBuilder;
25 import org.apache.archiva.maven2.metadata.MavenMetadataReader;
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.*;
34 import org.apache.archiva.metadata.repository.storage.maven2.ArtifactMetadataVersionComparator;
35 import org.apache.archiva.metadata.repository.storage.maven2.MavenProjectFacet;
36 import org.apache.archiva.model.ArchivaArtifact;
37 import org.apache.archiva.model.ArchivaRepositoryMetadata;
38 import org.apache.archiva.proxy.ProxyRegistry;
39 import org.apache.archiva.proxy.model.RepositoryProxyHandler;
40 import org.apache.archiva.redback.components.cache.Cache;
41 import org.apache.archiva.repository.ManagedRepositoryContent;
42 import org.apache.archiva.repository.ReleaseScheme;
43 import org.apache.archiva.repository.RepositoryException;
44 import org.apache.archiva.repository.RepositoryNotFoundException;
45 import org.apache.archiva.repository.metadata.MetadataTools;
46 import org.apache.archiva.rest.api.model.*;
47 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
48 import org.apache.archiva.rest.api.services.BrowseService;
49 import org.apache.archiva.rest.services.utils.ArtifactContentEntryComparator;
50 import org.apache.archiva.security.ArchivaSecurityException;
51 import org.apache.archiva.xml.XMLException;
52 import org.apache.commons.collections4.CollectionUtils;
53 import org.apache.commons.io.IOUtils;
54 import org.apache.commons.lang.StringUtils;
55 import org.springframework.stereotype.Service;
56
57 import javax.inject.Inject;
58 import javax.inject.Named;
59 import javax.ws.rs.core.Response;
60 import java.io.IOException;
61 import java.io.InputStream;
62 import java.nio.charset.Charset;
63 import java.nio.file.Files;
64 import java.nio.file.Path;
65 import java.util.*;
66 import java.util.jar.JarEntry;
67 import java.util.jar.JarFile;
68 import java.util.zip.ZipEntry;
69
70 /**
71  * @author Olivier Lamy
72  * @since 1.4-M3
73  */
74 @Service( "browseService#rest" )
75 public class DefaultBrowseService
76     extends AbstractRestService
77     implements BrowseService
78 {
79
80     private final Charset ARTIFACT_CONTENT_ENCODING=Charset.forName( "UTF-8" );
81
82     @Inject
83     private DependencyTreeBuilder dependencyTreeBuilder;
84
85     @Inject
86     ProxyRegistry proxyRegistry;
87
88     @Inject
89     @Named( value = "browse#versionMetadata" )
90     private Cache<String, ProjectVersionMetadata> versionMetadataCache;
91
92     private ManagedRepositoryContent getManagedRepositoryContent(String id) throws RepositoryException
93     {
94         org.apache.archiva.repository.ManagedRepository repo = repositoryRegistry.getManagedRepository( id );
95         if (repo==null) {
96             throw new RepositoryException( "Could not find repository "+id );
97         }
98         return repo.getContent();
99     }
100
101     @Override
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 = new LinkedHashSet<String>();
113         RepositorySession repositorySession = repositorySessionFactory.createSession();
114         try
115         {
116             MetadataResolver metadataResolver = repositorySession.getResolver();
117
118             for ( String repoId : selectedRepos )
119             {
120                 namespacesToCollapse.addAll( metadataResolver.resolveRootNamespaces( repositorySession, repoId ) );
121             }
122             for ( String n : namespacesToCollapse )
123             {
124                 // TODO: check performance of this
125                 namespaces.add( collapseNamespaces( repositorySession, metadataResolver, selectedRepos, n ) );
126             }
127         }
128         catch ( MetadataResolutionException e )
129         {
130             throw new ArchivaRestServiceException( e.getMessage(),
131                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
132         }
133         finally
134         {
135             repositorySession.close();
136         }
137
138         List<BrowseResultEntry> browseGroupResultEntries = new ArrayList<>( namespaces.size() );
139         for ( String namespace : namespaces )
140         {
141             browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ) );
142         }
143
144         Collections.sort( browseGroupResultEntries );
145         return new BrowseResult( browseGroupResultEntries );
146     }
147
148     @Override
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<>();
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<>();
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<>();
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 = new ArrayList<>( namespaces.size() + projects.size() );
191         for ( String namespace : namespaces )
192         {
193             browseGroupResultEntries.add( new BrowseResultEntry( namespace, false ).groupId( namespace ) );
194         }
195         for ( String project : projects )
196         {
197             browseGroupResultEntries.add(
198                 new BrowseResultEntry( groupId + '.' + project, true ).groupId( groupId ).artifactId( project ) );
199         }
200         Collections.sort( browseGroupResultEntries );
201         return new BrowseResult( browseGroupResultEntries );
202
203     }
204
205     @Override
206     public VersionsList getVersionsList( String groupId, String artifactId, String repositoryId )
207         throws ArchivaRestServiceException
208     {
209         List<String> selectedRepos = getSelectedRepos( repositoryId );
210
211         try
212         {
213             Collection<String> versions = getVersions( selectedRepos, groupId, artifactId );
214             return new VersionsList( new ArrayList<>( versions ) );
215         }
216         catch ( MetadataResolutionException e )
217         {
218             throw new ArchivaRestServiceException( e.getMessage(),
219                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
220         }
221
222     }
223
224     private Collection<String> getVersions( List<String> selectedRepos, String groupId, String artifactId )
225         throws MetadataResolutionException
226
227     {
228         RepositorySession repositorySession = repositorySessionFactory.createSession();
229         try
230         {
231             MetadataResolver metadataResolver = repositorySession.getResolver();
232
233             Set<String> versions = new LinkedHashSet<String>();
234
235             for ( String repoId : selectedRepos )
236             {
237                 Collection<String> projectVersions =
238                     metadataResolver.resolveProjectVersions( repositorySession, repoId, groupId, artifactId );
239                 versions.addAll( projectVersions );
240             }
241
242             List<String> sortedVersions = new ArrayList<>( versions );
243
244             Collections.sort( sortedVersions, VersionComparator.getInstance() );
245
246             return sortedVersions;
247         }
248         finally
249         {
250             repositorySession.close();
251         }
252     }
253
254     @Override
255     public ProjectVersionMetadata getProjectMetadata( String groupId, String artifactId, String version,
256                                                       String repositoryId )
257         throws ArchivaRestServiceException
258     {
259         List<String> selectedRepos = getSelectedRepos( repositoryId );
260
261         RepositorySession repositorySession = null;
262         try
263         {
264             repositorySession = repositorySessionFactory.createSession();
265
266             MetadataResolver metadataResolver = repositorySession.getResolver();
267
268             ProjectVersionMetadata versionMetadata = null;
269             for ( String repoId : selectedRepos )
270             {
271                 if ( versionMetadata == null || versionMetadata.isIncomplete() )
272                 {
273                     try
274                     {
275                         ProjectVersionMetadata versionMetadataTmp =
276                             metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId, artifactId,
277                                                                     version );
278
279                         if ( versionMetadata == null && versionMetadataTmp != null )
280                         {
281                             versionMetadata = versionMetadataTmp;
282                         }
283
284
285                     }
286                     catch ( MetadataResolutionException e )
287                     {
288                         log.warn( "Skipping invalid metadata while compiling shared model for {}:{} in repo {}: {}",
289                                   groupId, artifactId, repoId, e.getMessage() );
290                     }
291                 }
292             }
293
294             return versionMetadata;
295         }
296         finally
297         {
298             if ( repositorySession != null )
299             {
300                 repositorySession.close();
301             }
302         }
303
304     }
305
306     @Override
307     public ProjectVersionMetadata getProjectVersionMetadata( String groupId, String artifactId, String repositoryId )
308         throws ArchivaRestServiceException
309     {
310
311         List<String> selectedRepos = getSelectedRepos( repositoryId );
312
313         RepositorySession repositorySession = null;
314         try
315         {
316
317             Collection<String> projectVersions = getVersions( selectedRepos, groupId, artifactId );
318
319             repositorySession = repositorySessionFactory.createSession();
320
321             MetadataResolver metadataResolver = repositorySession.getResolver();
322
323             ProjectVersionMetadata sharedModel = new ProjectVersionMetadata();
324
325             MavenProjectFacet mavenFacet = new MavenProjectFacet();
326             mavenFacet.setGroupId( groupId );
327             mavenFacet.setArtifactId( artifactId );
328             sharedModel.addFacet( mavenFacet );
329
330             boolean isFirstVersion = true;
331
332             for ( String version : projectVersions )
333             {
334                 ProjectVersionMetadata versionMetadata = null;
335                 for ( String repoId : selectedRepos )
336                 {
337                     if ( versionMetadata == null || versionMetadata.isIncomplete() )
338                     {
339                         try
340                         {
341                             ProjectVersionMetadata projectVersionMetadataResolved = null;
342                             boolean useCache = !StringUtils.endsWith( version, VersionUtil.SNAPSHOT );
343                             String cacheKey = null;
344                             boolean cacheToUpdate = false;
345                             // FIXME a bit maven centric!!!
346                             // not a snapshot so get it from cache
347                             if ( useCache )
348                             {
349                                 cacheKey = repoId + groupId + artifactId + version;
350                                 projectVersionMetadataResolved = versionMetadataCache.get( cacheKey );
351                             }
352                             if ( useCache && projectVersionMetadataResolved != null )
353                             {
354                                 versionMetadata = projectVersionMetadataResolved;
355                             }
356                             else
357                             {
358                                 projectVersionMetadataResolved =
359                                     metadataResolver.resolveProjectVersion( repositorySession, repoId, groupId,
360                                                                             artifactId, version );
361                                 versionMetadata = projectVersionMetadataResolved;
362                                 cacheToUpdate = true;
363                             }
364
365                             if ( useCache && cacheToUpdate )
366                             {
367                                 versionMetadataCache.put( cacheKey, projectVersionMetadataResolved );
368                             }
369
370                         }
371                         catch ( MetadataResolutionException e )
372                         {
373                             log.error( "Skipping invalid metadata while compiling shared model for " + groupId + ":"
374                                            + artifactId + " in repo " + repoId + ": " + e.getMessage() );
375                         }
376                     }
377                 }
378
379                 if ( versionMetadata == null )
380                 {
381                     continue;
382                 }
383
384                 if ( isFirstVersion )
385                 {
386                     sharedModel = versionMetadata;
387                     sharedModel.setId( null );
388                 }
389                 else
390                 {
391                     MavenProjectFacet versionMetadataMavenFacet =
392                         (MavenProjectFacet) versionMetadata.getFacet( MavenProjectFacet.FACET_ID );
393                     if ( versionMetadataMavenFacet != null )
394                     {
395                         if ( mavenFacet.getPackaging() != null //
396                             && !StringUtils.equalsIgnoreCase( mavenFacet.getPackaging(),
397                                                               versionMetadataMavenFacet.getPackaging() ) )
398                         {
399                             mavenFacet.setPackaging( null );
400                         }
401                     }
402
403                     if ( StringUtils.isEmpty( sharedModel.getName() ) //
404                         && !StringUtils.isEmpty( versionMetadata.getName() ) )
405                     {
406                         sharedModel.setName( versionMetadata.getName() );
407                     }
408
409                     if ( sharedModel.getDescription() != null //
410                         && !StringUtils.equalsIgnoreCase( sharedModel.getDescription(),
411                                                           versionMetadata.getDescription() ) )
412                     {
413                         sharedModel.setDescription( StringUtils.isNotEmpty( versionMetadata.getDescription() )
414                                                         ? versionMetadata.getDescription()
415                                                         : "" );
416                     }
417
418                     if ( sharedModel.getIssueManagement() != null //
419                         && versionMetadata.getIssueManagement() != null //
420                         && !StringUtils.equalsIgnoreCase( sharedModel.getIssueManagement().getUrl(),
421                                                           versionMetadata.getIssueManagement().getUrl() ) )
422                     {
423                         sharedModel.setIssueManagement( versionMetadata.getIssueManagement() );
424                     }
425
426                     if ( sharedModel.getCiManagement() != null //
427                         && versionMetadata.getCiManagement() != null //
428                         && !StringUtils.equalsIgnoreCase( sharedModel.getCiManagement().getUrl(),
429                                                           versionMetadata.getCiManagement().getUrl() ) )
430                     {
431                         sharedModel.setCiManagement( versionMetadata.getCiManagement() );
432                     }
433
434                     if ( sharedModel.getOrganization() != null //
435                         && versionMetadata.getOrganization() != null //
436                         && !StringUtils.equalsIgnoreCase( sharedModel.getOrganization().getName(),
437                                                           versionMetadata.getOrganization().getName() ) )
438                     {
439                         sharedModel.setOrganization( versionMetadata.getOrganization() );
440                     }
441
442                     if ( sharedModel.getUrl() != null //
443                         && !StringUtils.equalsIgnoreCase( sharedModel.getUrl(), versionMetadata.getUrl() ) )
444                     {
445                         sharedModel.setUrl( versionMetadata.getUrl() );
446                     }
447                 }
448
449                 isFirstVersion = false;
450             }
451             return sharedModel;
452         }
453         catch ( MetadataResolutionException e )
454         {
455             throw new ArchivaRestServiceException( e.getMessage(),
456                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
457         }
458         finally
459         {
460             if ( repositorySession != null )
461             {
462                 repositorySession.close();
463             }
464         }
465     }
466
467     @Override
468     public List<TreeEntry> getTreeEntries( String groupId, String artifactId, String version, String repositoryId )
469         throws ArchivaRestServiceException
470     {
471         List<String> selectedRepos = getSelectedRepos( repositoryId );
472
473         try
474         {
475             return dependencyTreeBuilder.buildDependencyTree( selectedRepos, groupId, artifactId, version );
476         }
477         catch ( Exception e )
478         {
479             log.error( e.getMessage(), e );
480         }
481
482         return Collections.emptyList();
483     }
484
485     @Override
486     public List<ManagedRepository> getUserRepositories()
487         throws ArchivaRestServiceException
488     {
489         try
490         {
491             return userRepositories.getAccessibleRepositories( getPrincipal() );
492         }
493         catch ( ArchivaSecurityException e )
494         {
495             throw new ArchivaRestServiceException( "repositories.read.observable.error",
496                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
497         }
498     }
499
500     @Override
501     public List<ManagedRepository> getUserManagableRepositories() throws ArchivaRestServiceException {
502         try
503         {
504             return userRepositories.getManagableRepositories( getPrincipal() );
505         }
506         catch ( ArchivaSecurityException e )
507         {
508             throw new ArchivaRestServiceException( "repositories.read.managable.error",
509                     Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
510         }
511     }
512
513     @Override
514     public List<Artifact> getDependees( String groupId, String artifactId, String version, String repositoryId )
515         throws ArchivaRestServiceException
516     {
517         List<ProjectVersionReference> references = new ArrayList<>();
518         // TODO: what if we get duplicates across repositories?
519         RepositorySession repositorySession = repositorySessionFactory.createSession();
520         try
521         {
522             MetadataResolver metadataResolver = repositorySession.getResolver();
523             for ( String repoId : getObservableRepos() )
524             {
525                 // TODO: what about if we want to see this irrespective of version?
526                 references.addAll(
527                     metadataResolver.resolveProjectReferences( repositorySession, repoId, groupId, artifactId,
528                                                                version ) );
529             }
530         }
531         catch ( MetadataResolutionException e )
532         {
533             throw new ArchivaRestServiceException( e.getMessage(),
534                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
535         }
536         finally
537         {
538             repositorySession.close();
539         }
540
541         List<Artifact> artifacts = new ArrayList<>( references.size() );
542
543         for ( ProjectVersionReference projectVersionReference : references )
544         {
545             artifacts.add( new Artifact( projectVersionReference.getNamespace(), projectVersionReference.getProjectId(),
546                                          projectVersionReference.getProjectVersion() ) );
547         }
548         return artifacts;
549     }
550
551     @Override
552     public List<Entry> getMetadatas( String groupId, String artifactId, String version, String repositoryId )
553         throws ArchivaRestServiceException
554     {
555         ProjectVersionMetadata projectVersionMetadata =
556             getProjectMetadata( groupId, artifactId, version, repositoryId );
557         if ( projectVersionMetadata == null )
558         {
559             return Collections.emptyList();
560         }
561         MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
562
563         if ( metadataFacet == null )
564         {
565             return Collections.emptyList();
566         }
567         Map<String, String> map = metadataFacet.toProperties();
568         List<Entry> entries = new ArrayList<>( map.size() );
569
570         for ( Map.Entry<String, String> entry : map.entrySet() )
571         {
572             entries.add( new Entry( entry.getKey(), entry.getValue() ) );
573         }
574
575         return entries;
576     }
577
578     @Override
579     public Boolean addMetadata( String groupId, String artifactId, String version, String key, String value,
580                                 String repositoryId )
581         throws ArchivaRestServiceException
582     {
583         ProjectVersionMetadata projectVersionMetadata =
584             getProjectMetadata( groupId, artifactId, version, repositoryId );
585
586         if ( projectVersionMetadata == null )
587         {
588             return Boolean.FALSE;
589         }
590
591         Map<String, String> properties = new HashMap<>();
592
593         MetadataFacet metadataFacet = projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
594
595         if ( metadataFacet != null && metadataFacet.toProperties() != null )
596         {
597             properties.putAll( metadataFacet.toProperties() );
598         }
599         else
600         {
601             metadataFacet = new GenericMetadataFacet();
602         }
603
604         properties.put( key, value );
605
606         metadataFacet.fromProperties( properties );
607
608         projectVersionMetadata.addFacet( metadataFacet );
609
610         RepositorySession repositorySession = repositorySessionFactory.createSession();
611
612         try
613         {
614             MetadataRepository metadataRepository = repositorySession.getRepository();
615
616             metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
617
618             repositorySession.save();
619         }
620         catch ( MetadataRepositoryException e )
621         {
622             log.error( e.getMessage(), e );
623             throw new ArchivaRestServiceException( e.getMessage(),
624                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
625         }
626         finally
627         {
628             repositorySession.close();
629         }
630         return Boolean.TRUE;
631     }
632
633     @Override
634     public Boolean deleteMetadata( String groupId, String artifactId, String version, String key, String repositoryId )
635         throws ArchivaRestServiceException
636     {
637         ProjectVersionMetadata projectVersionMetadata =
638             getProjectMetadata( groupId, artifactId, version, repositoryId );
639
640         if ( projectVersionMetadata == null )
641         {
642             return Boolean.FALSE;
643         }
644
645         GenericMetadataFacet metadataFacet =
646             (GenericMetadataFacet) projectVersionMetadata.getFacet( GenericMetadataFacet.FACET_ID );
647
648         if ( metadataFacet != null && metadataFacet.toProperties() != null )
649         {
650             Map<String, String> properties = metadataFacet.toProperties();
651             properties.remove( key );
652             metadataFacet.setAdditionalProperties( properties );
653         }
654         else
655         {
656             return Boolean.TRUE;
657         }
658
659         RepositorySession repositorySession = repositorySessionFactory.createSession();
660
661         try
662         {
663             MetadataRepository metadataRepository = repositorySession.getRepository();
664
665             metadataRepository.updateProjectVersion( repositoryId, groupId, artifactId, projectVersionMetadata );
666
667             repositorySession.save();
668         }
669         catch ( MetadataRepositoryException e )
670         {
671             log.error( e.getMessage(), e );
672             throw new ArchivaRestServiceException( e.getMessage(),
673                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
674         }
675         finally
676         {
677             repositorySession.close();
678         }
679         return Boolean.TRUE;
680     }
681
682     @Override
683     public List<ArtifactContentEntry> getArtifactContentEntries( String groupId, String artifactId, String version,
684                                                                  String classifier, String type, String path,
685                                                                  String repositoryId )
686         throws ArchivaRestServiceException
687     {
688         List<String> selectedRepos = getSelectedRepos( repositoryId );
689         try
690         {
691             for ( String repoId : selectedRepos )
692             {
693
694                 ManagedRepositoryContent managedRepositoryContent =
695                     getManagedRepositoryContent( repoId );
696                 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, classifier,
697                                                                        StringUtils.isEmpty( type ) ? "jar" : type,
698                                                                        repoId );
699                 Path file = managedRepositoryContent.toFile( archivaArtifact );
700                 if ( Files.exists(file) )
701                 {
702                     return readFileEntries( file, path, repoId );
703                 }
704             }
705         }
706         catch ( IOException e )
707         {
708             log.error( e.getMessage(), e );
709             throw new ArchivaRestServiceException( e.getMessage(),
710                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
711         }
712         catch ( RepositoryNotFoundException e )
713         {
714             log.error( e.getMessage(), e );
715             throw new ArchivaRestServiceException( e.getMessage(),
716                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
717         }
718         catch ( RepositoryException e )
719         {
720             log.error( e.getMessage(), e );
721             throw new ArchivaRestServiceException( e.getMessage(),
722                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
723         }
724         return Collections.emptyList();
725     }
726
727     @Override
728     public List<Artifact> getArtifactDownloadInfos( String groupId, String artifactId, String version,
729                                                     String repositoryId )
730         throws ArchivaRestServiceException
731     {
732         List<String> selectedRepos = getSelectedRepos( repositoryId );
733
734         List<Artifact> artifactDownloadInfos = new ArrayList<>();
735
736         try (RepositorySession session = repositorySessionFactory.createSession())
737         {
738             MetadataResolver metadataResolver = session.getResolver();
739             for ( String repoId : selectedRepos )
740             {
741                 List<ArtifactMetadata> artifacts = new ArrayList<>(
742                     metadataResolver.resolveArtifacts( session, repoId, groupId, artifactId, version ) );
743                 Collections.sort( artifacts, ArtifactMetadataVersionComparator.INSTANCE );
744                 if ( artifacts != null && !artifacts.isEmpty() )
745                 {
746                     return buildArtifacts( artifacts, repoId );
747                 }
748             }
749         }
750         catch ( MetadataResolutionException e )
751         {
752             log.error( e.getMessage(), e );
753             throw new ArchivaRestServiceException( e.getMessage(),
754                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
755         }
756
757         return artifactDownloadInfos;
758     }
759
760     @Override
761     public ArtifactContent getArtifactContentText( String groupId, String artifactId, String version, String classifier,
762                                                    String type, String path, String repositoryId )
763         throws ArchivaRestServiceException
764     {
765         List<String> selectedRepos = getSelectedRepos( repositoryId );
766         try
767         {
768             for ( String repoId : selectedRepos )
769             {
770
771                 ManagedRepositoryContent managedRepositoryContent = null;
772                 try
773                 {
774                     managedRepositoryContent = getManagedRepositoryContent( repoId );
775                 }
776                 catch ( RepositoryException e )
777                 {
778                     log.error("No repository content found for "+repoId);
779                     continue;
780                 }
781                 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version, classifier,
782                                                                        StringUtils.isEmpty( type ) ? "jar" : type,
783                                                                        repoId );
784                 Path file = managedRepositoryContent.toFile( archivaArtifact );
785                 if ( !Files.exists(file) )
786                 {
787                     log.debug( "file: {} not exists for repository: {} try next repository", file, repoId );
788                     continue;
789                 }
790                 if ( StringUtils.isNotBlank( path ) )
791                 {
792                     // zip entry of the path -> path must a real file entry of the archive
793                     JarFile jarFile = new JarFile( file.toFile() );
794                     ZipEntry zipEntry = jarFile.getEntry( path );
795                     try (InputStream inputStream = jarFile.getInputStream( zipEntry ))
796                     {
797                         return new ArtifactContent( IOUtils.toString( inputStream, ARTIFACT_CONTENT_ENCODING ), repoId );
798                     }
799                     finally
800                     {
801                         closeQuietly( jarFile );
802                     }
803                 }
804                 return new ArtifactContent( new String(Files.readAllBytes( file ), ARTIFACT_CONTENT_ENCODING), repoId );
805             }
806         }
807         catch ( IOException e )
808         {
809             log.error( e.getMessage(), e );
810             throw new ArchivaRestServiceException( e.getMessage(),
811                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
812         }
813         log.debug( "artifact: {}:{}:{}:{}:{} not found", groupId, artifactId, version, classifier, type );
814         // 404 ?
815         return new ArtifactContent();
816     }
817
818     @Override
819     public Boolean artifactAvailable( String groupId, String artifactId, String version, String classifier,
820                                       String repositoryId )
821         throws ArchivaRestServiceException
822     {
823         List<String> selectedRepos = getSelectedRepos( repositoryId );
824
825         boolean snapshot = VersionUtil.isSnapshot( version );
826
827         try
828         {
829             for ( String repoId : selectedRepos )
830             {
831
832                 org.apache.archiva.repository.ManagedRepository managedRepo = repositoryRegistry.getManagedRepository(repoId);
833                 if (!proxyRegistry.hasHandler(managedRepo.getType())) {
834                     throw new RepositoryException( "No proxy handler found for repository type "+managedRepo.getType());
835                 }
836                 RepositoryProxyHandler proxyHandler = proxyRegistry.getHandler(managedRepo.getType()).get(0);
837                 if ( ( snapshot && !managedRepo.getActiveReleaseSchemes().contains(ReleaseScheme.SNAPSHOT) ) || ( !snapshot
838                     && managedRepo.getActiveReleaseSchemes().contains(ReleaseScheme.SNAPSHOT) ) )
839                 {
840                     continue;
841                 }
842                 ManagedRepositoryContent managedRepositoryContent = getManagedRepositoryContent( repoId );
843
844                 // FIXME default to jar which can be wrong for war zip etc....
845                 ArchivaArtifact archivaArtifact = new ArchivaArtifact( groupId, artifactId, version,
846                                                                        StringUtils.isEmpty( classifier )
847                                                                            ? ""
848                                                                            : classifier, "jar", repoId );
849                 Path file = managedRepositoryContent.toFile( archivaArtifact );
850
851                 if ( file != null && Files.exists(file) )
852                 {
853                     return true;
854                 }
855
856                 // in case of SNAPSHOT we can have timestamped version locally !
857                 if ( StringUtils.endsWith( version, VersionUtil.SNAPSHOT ) )
858                 {
859                     Path metadataFile = file.getParent().resolve(MetadataTools.MAVEN_METADATA );
860                     if ( Files.exists(metadataFile) )
861                     {
862                         try
863                         {
864                             ArchivaRepositoryMetadata archivaRepositoryMetadata =
865                                 MavenMetadataReader.read( metadataFile );
866                             int buildNumber = archivaRepositoryMetadata.getSnapshotVersion().getBuildNumber();
867                             String timeStamp = archivaRepositoryMetadata.getSnapshotVersion().getTimestamp();
868                             // rebuild file name with timestamped version and build number
869                             String timeStampFileName = new StringBuilder( artifactId ).append( '-' ) //
870                                 .append( StringUtils.remove( version, "-" + VersionUtil.SNAPSHOT ) ) //
871                                 .append( '-' ).append( timeStamp ) //
872                                 .append( '-' ).append( Integer.toString( buildNumber ) ) //
873                                 .append( ( StringUtils.isEmpty( classifier ) ? "" : "-" + classifier ) ) //
874                                 .append( ".jar" ).toString();
875
876                             Path timeStampFile = file.getParent().resolve( timeStampFileName );
877                             log.debug( "try to find timestamped snapshot version file: {}", timeStampFile.toAbsolutePath() );
878                             if ( Files.exists(timeStampFile) )
879                             {
880                                 return true;
881                             }
882                         }
883                         catch ( XMLException e )
884                         {
885                             log.warn( "skip fail to find timestamped snapshot file: {}", e.getMessage() );
886                         }
887                     }
888                 }
889
890                 String path = managedRepositoryContent.toPath( archivaArtifact );
891
892                 file = proxyHandler.fetchFromProxies( managedRepositoryContent, path );
893
894                 if ( file != null && Files.exists(file) )
895                 {
896                     // download pom now
897                     String pomPath = StringUtils.substringBeforeLast( path, ".jar" ) + ".pom";
898                     proxyHandler.fetchFromProxies( managedRepositoryContent, pomPath );
899                     return true;
900                 }
901             }
902         } catch ( RepositoryException e )
903         {
904             log.error( e.getMessage(), e );
905             throw new ArchivaRestServiceException( e.getMessage(),
906                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
907         }
908
909         return false;
910     }
911
912     @Override
913     public Boolean artifactAvailable( String groupId, String artifactId, String version, String repositoryId )
914         throws ArchivaRestServiceException
915     {
916         return artifactAvailable( groupId, artifactId, version, null, repositoryId );
917     }
918
919     @Override
920     public List<Artifact> getArtifacts( String repositoryId )
921         throws ArchivaRestServiceException
922     {
923         RepositorySession repositorySession = repositorySessionFactory.createSession();
924         try
925         {
926             List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifacts( repositoryId );
927             return buildArtifacts( artifactMetadatas, repositoryId );
928         }
929         catch ( MetadataRepositoryException e )
930         {
931             throw new ArchivaRestServiceException( e.getMessage(), e );
932         }
933         finally
934         {
935             repositorySession.close();
936         }
937     }
938
939     @Override
940     public List<Artifact> getArtifactsByProjectVersionMetadata( String key, String value, String repositoryId )
941         throws ArchivaRestServiceException
942     {
943         RepositorySession repositorySession = repositorySessionFactory.createSession();
944         try
945         {
946             List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifactsByProjectVersionMetadata( key, value, repositoryId );
947             return buildArtifacts( artifactMetadatas, repositoryId );
948         }
949         catch ( MetadataRepositoryException e )
950         {
951             throw new ArchivaRestServiceException( e.getMessage(), e );
952         }
953         finally
954         {
955             repositorySession.close();
956         }
957     }
958
959     @Override
960     public List<Artifact> getArtifactsByMetadata( String key, String value, String repositoryId )
961         throws ArchivaRestServiceException
962     {
963         RepositorySession repositorySession = repositorySessionFactory.createSession();
964         try
965         {
966             List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifactsByMetadata( key, value, repositoryId );
967             return buildArtifacts( artifactMetadatas, repositoryId );
968         }
969         catch ( MetadataRepositoryException e )
970         {
971             throw new ArchivaRestServiceException( e.getMessage(), e );
972         }
973         finally
974         {
975             repositorySession.close();
976         }
977     }
978
979     @Override
980     public List<Artifact> getArtifactsByProperty( String key, String value, String repositoryId )
981         throws ArchivaRestServiceException
982     {
983         RepositorySession repositorySession = repositorySessionFactory.createSession();
984         try
985         {
986             List<ArtifactMetadata> artifactMetadatas = repositorySession.getRepository().getArtifactsByProperty( key, value, repositoryId );
987             return buildArtifacts( artifactMetadatas, repositoryId );
988         }
989         catch ( MetadataRepositoryException e )
990         {
991             throw new ArchivaRestServiceException( e.getMessage(), e );
992         }
993         finally
994         {
995             repositorySession.close();
996         }
997     }
998
999     @Override
1000     public Boolean importMetadata( MetadataAddRequest metadataAddRequest, String repositoryId )
1001         throws ArchivaRestServiceException
1002     {
1003         boolean result = true;
1004         for ( Map.Entry<String, String> metadata : metadataAddRequest.getMetadatas().entrySet() )
1005         {
1006             result = addMetadata( metadataAddRequest.getGroupId(), metadataAddRequest.getArtifactId(),
1007                                   metadataAddRequest.getVersion(), metadata.getKey(), metadata.getValue(),
1008                                   repositoryId );
1009             if ( !result )
1010             {
1011                 break;
1012             }
1013         }
1014         return result;
1015     }
1016
1017     @Override
1018     public List<Artifact> searchArtifacts( String text, String repositoryId, Boolean exact )
1019         throws ArchivaRestServiceException
1020     {
1021         RepositorySession repositorySession = repositorySessionFactory.createSession();
1022         try
1023         {
1024             List<ArtifactMetadata> artifactMetadatas =
1025                 repositorySession.getRepository().searchArtifacts( text, repositoryId, exact == null ? false : exact );
1026             return buildArtifacts( artifactMetadatas, repositoryId );
1027         }
1028         catch ( MetadataRepositoryException e )
1029         {
1030             throw new ArchivaRestServiceException( e.getMessage(), e );
1031         }
1032         finally
1033         {
1034             repositorySession.close();
1035         }
1036     }
1037
1038     @Override
1039     public List<Artifact> searchArtifacts( String key, String text, String repositoryId, Boolean exact )
1040         throws ArchivaRestServiceException
1041     {
1042         RepositorySession repositorySession = repositorySessionFactory.createSession();
1043         try
1044         {
1045             List<ArtifactMetadata> artifactMetadatas =
1046                 repositorySession.getRepository().searchArtifacts( key, text, repositoryId, exact == null ? false : exact );
1047             return buildArtifacts( artifactMetadatas, repositoryId );
1048         }
1049         catch ( MetadataRepositoryException e )
1050         {
1051             throw new ArchivaRestServiceException( e.getMessage(), e );
1052         }
1053         finally
1054         {
1055             repositorySession.close();
1056         }
1057     }
1058
1059     //---------------------------
1060     // internals
1061     //---------------------------
1062
1063     private void closeQuietly( JarFile jarFile )
1064     {
1065         if ( jarFile != null )
1066         {
1067             try
1068             {
1069                 jarFile.close();
1070             }
1071             catch ( IOException e )
1072             {
1073                 log.warn( "ignore error closing jarFile {}", jarFile.getName() );
1074             }
1075         }
1076     }
1077
1078     protected List<ArtifactContentEntry> readFileEntries(final Path file, final String filterPath, final String repoId )
1079         throws IOException
1080     {
1081         String cleanedfilterPath = filterPath==null ? "" : (StringUtils.startsWith(filterPath, "/") ?
1082                 StringUtils.substringAfter(filterPath, "/") : filterPath);
1083         Map<String, ArtifactContentEntry> artifactContentEntryMap = new HashMap<>();
1084         int filterDepth = StringUtils.countMatches( cleanedfilterPath, "/" );
1085         if (!StringUtils.endsWith(cleanedfilterPath,"/") && !StringUtils.isEmpty(cleanedfilterPath)) {
1086             filterDepth++;
1087         }
1088         JarFile jarFile = new JarFile( file.toFile() );
1089         try
1090         {
1091             Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries();
1092             while ( jarEntryEnumeration.hasMoreElements() )
1093             {
1094                 JarEntry currentEntry = jarEntryEnumeration.nextElement();
1095                 String cleanedEntryName = StringUtils.endsWith( currentEntry.getName(), "/" ) ? //
1096                     StringUtils.substringBeforeLast( currentEntry.getName(), "/" ) : currentEntry.getName();
1097                 String entryRootPath = getRootPath( cleanedEntryName );
1098                 int depth = StringUtils.countMatches( cleanedEntryName, "/" );
1099                 if ( StringUtils.isEmpty( cleanedfilterPath ) //
1100                     && !artifactContentEntryMap.containsKey( entryRootPath ) //
1101                     && depth == filterDepth )
1102                 {
1103
1104                     artifactContentEntryMap.put( entryRootPath,
1105                                                  new ArtifactContentEntry( entryRootPath, !currentEntry.isDirectory(),
1106                                                                            depth, repoId ) );
1107                 }
1108                 else
1109                 {
1110                     if ( StringUtils.startsWith( cleanedEntryName, cleanedfilterPath ) //
1111                         && ( depth == filterDepth || ( !currentEntry.isDirectory() && depth == filterDepth ) ) )
1112                     {
1113                         artifactContentEntryMap.put( cleanedEntryName, new ArtifactContentEntry( cleanedEntryName,
1114                                                                                                  !currentEntry.isDirectory(),
1115                                                                                                  depth, repoId ) );
1116                     }
1117                 }
1118             }
1119
1120             if ( StringUtils.isNotEmpty( cleanedfilterPath ) )
1121             {
1122                 Map<String, ArtifactContentEntry> filteredArtifactContentEntryMap = new HashMap<>();
1123
1124                 for ( Map.Entry<String, ArtifactContentEntry> entry : artifactContentEntryMap.entrySet() )
1125                 {
1126                     filteredArtifactContentEntryMap.put( entry.getKey(), entry.getValue() );
1127                 }
1128
1129                 List<ArtifactContentEntry> sorted = getSmallerDepthEntries( filteredArtifactContentEntryMap );
1130                 if ( sorted == null )
1131                 {
1132                     return Collections.emptyList();
1133                 }
1134                 Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
1135                 return sorted;
1136             }
1137         }
1138         finally
1139         {
1140             if ( jarFile != null )
1141             {
1142                 jarFile.close();
1143             }
1144         }
1145         List<ArtifactContentEntry> sorted = new ArrayList<>( artifactContentEntryMap.values() );
1146         Collections.sort( sorted, ArtifactContentEntryComparator.INSTANCE );
1147         return sorted;
1148     }
1149
1150     private List<ArtifactContentEntry> getSmallerDepthEntries( Map<String, ArtifactContentEntry> entries )
1151     {
1152         int smallestDepth = Integer.MAX_VALUE;
1153         Map<Integer, List<ArtifactContentEntry>> perDepthList = new HashMap<>();
1154         for ( Map.Entry<String, ArtifactContentEntry> entry : entries.entrySet() )
1155         {
1156
1157             ArtifactContentEntry current = entry.getValue();
1158
1159             if ( current.getDepth() < smallestDepth )
1160             {
1161                 smallestDepth = current.getDepth();
1162             }
1163
1164             List<ArtifactContentEntry> currentList = perDepthList.get( current.getDepth() );
1165
1166             if ( currentList == null )
1167             {
1168                 currentList = new ArrayList<>();
1169                 currentList.add( current );
1170                 perDepthList.put( current.getDepth(), currentList );
1171             }
1172             else
1173             {
1174                 currentList.add( current );
1175             }
1176
1177         }
1178
1179         return perDepthList.get( smallestDepth );
1180     }
1181
1182     /**
1183      * @param path
1184      * @return org/apache -&gt; org , org -&gt; org
1185      */
1186     private String getRootPath( String path )
1187     {
1188         if ( StringUtils.contains( path, '/' ) )
1189         {
1190             return StringUtils.substringBefore( path, "/" );
1191         }
1192         return path;
1193     }
1194
1195     private List<String> getSelectedRepos( String repositoryId )
1196         throws ArchivaRestServiceException
1197     {
1198
1199         List<String> selectedRepos = getObservableRepos();
1200
1201         if ( CollectionUtils.isEmpty( selectedRepos ) )
1202         {
1203             return Collections.emptyList();
1204         }
1205
1206         if ( StringUtils.isNotEmpty( repositoryId ) )
1207         {
1208             // check user has karma on the repository
1209             if ( !selectedRepos.contains( repositoryId ) )
1210             {
1211                 throw new ArchivaRestServiceException( "browse.root.groups.repositoy.denied",
1212                                                        Response.Status.FORBIDDEN.getStatusCode(), null );
1213             }
1214             selectedRepos = Collections.singletonList( repositoryId );
1215         }
1216         return selectedRepos;
1217     }
1218
1219
1220     private String collapseNamespaces( RepositorySession repositorySession, MetadataResolver metadataResolver,
1221                                        Collection<String> repoIds, String n )
1222         throws MetadataResolutionException
1223     {
1224         Set<String> subNamespaces = new LinkedHashSet<String>();
1225         for ( String repoId : repoIds )
1226         {
1227             subNamespaces.addAll( metadataResolver.resolveNamespaces( repositorySession, repoId, n ) );
1228         }
1229         if ( subNamespaces.size() != 1 )
1230         {
1231             log.debug( "{} is not collapsible as it has sub-namespaces: {}", n, subNamespaces );
1232             return n;
1233         }
1234         else
1235         {
1236             for ( String repoId : repoIds )
1237             {
1238                 Collection<String> projects = metadataResolver.resolveProjects( repositorySession, repoId, n );
1239                 if ( projects != null && !projects.isEmpty() )
1240                 {
1241                     log.debug( "{} is not collapsible as it has projects", n );
1242                     return n;
1243                 }
1244             }
1245             return collapseNamespaces( repositorySession, metadataResolver, repoIds,
1246                                        n + "." + subNamespaces.iterator().next() );
1247         }
1248     }
1249
1250     public Cache<String, ProjectVersionMetadata> getVersionMetadataCache()
1251     {
1252         return versionMetadataCache;
1253     }
1254
1255     public void setVersionMetadataCache( Cache<String, ProjectVersionMetadata> versionMetadataCache )
1256     {
1257         this.versionMetadataCache = versionMetadataCache;
1258     }
1259 }