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