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