]> source.dussan.org Git - archiva.git/blob
697cadc60110b86549a444c194dd89763c20980c
[archiva.git] /
1 package org.apache.archiva.rest.services;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *   http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import org.apache.archiva.admin.model.RepositoryAdminException;
23 import org.apache.archiva.admin.model.admin.ArchivaAdministration;
24 import org.apache.archiva.admin.model.beans.ManagedRepository;
25 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
26 import org.apache.archiva.checksum.ChecksumAlgorithm;
27 import org.apache.archiva.checksum.ChecksummedFile;
28 import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
29 import org.apache.archiva.common.utils.VersionComparator;
30 import org.apache.archiva.common.utils.VersionUtil;
31 import org.apache.archiva.maven2.metadata.MavenMetadataReader;
32 import org.apache.archiva.maven2.model.Artifact;
33 import org.apache.archiva.metadata.model.ArtifactMetadata;
34 import org.apache.archiva.metadata.model.facets.AuditEvent;
35 import org.apache.archiva.metadata.model.maven2.MavenArtifactFacet;
36 import org.apache.archiva.metadata.repository.*;
37 import org.apache.archiva.model.ArchivaRepositoryMetadata;
38 import org.apache.archiva.model.ArtifactReference;
39 import org.apache.archiva.model.VersionedReference;
40 import org.apache.archiva.redback.authentication.AuthenticationResult;
41 import org.apache.archiva.redback.authorization.AuthorizationException;
42 import org.apache.archiva.redback.components.cache.Cache;
43 import org.apache.archiva.redback.components.taskqueue.TaskQueueException;
44 import org.apache.archiva.redback.system.DefaultSecuritySession;
45 import org.apache.archiva.redback.system.SecuritySession;
46 import org.apache.archiva.redback.system.SecuritySystem;
47 import org.apache.archiva.redback.users.User;
48 import org.apache.archiva.redback.users.UserManagerException;
49 import org.apache.archiva.redback.users.UserNotFoundException;
50 import org.apache.archiva.repository.*;
51 import org.apache.archiva.repository.events.RepositoryListener;
52 import org.apache.archiva.repository.metadata.MetadataTools;
53 import org.apache.archiva.repository.metadata.RepositoryMetadataException;
54 import org.apache.archiva.repository.metadata.RepositoryMetadataWriter;
55 import org.apache.archiva.repository.scanner.RepositoryScanStatistics;
56 import org.apache.archiva.repository.scanner.RepositoryScanner;
57 import org.apache.archiva.repository.scanner.RepositoryScannerException;
58 import org.apache.archiva.repository.scanner.RepositoryScannerInstance;
59 import org.apache.archiva.rest.api.model.ArtifactTransferRequest;
60 import org.apache.archiva.rest.api.model.StringList;
61 import org.apache.archiva.rest.api.services.ArchivaRestServiceException;
62 import org.apache.archiva.rest.api.services.RepositoriesService;
63 import org.apache.archiva.scheduler.ArchivaTaskScheduler;
64 import org.apache.archiva.scheduler.indexing.ArchivaIndexingTaskExecutor;
65 import org.apache.archiva.scheduler.indexing.ArtifactIndexingTask;
66 import org.apache.archiva.scheduler.indexing.DownloadRemoteIndexException;
67 import org.apache.archiva.scheduler.indexing.DownloadRemoteIndexScheduler;
68 import org.apache.archiva.scheduler.repository.model.RepositoryTask;
69 import org.apache.archiva.security.ArchivaSecurityException;
70 import org.apache.archiva.security.common.ArchivaRoleConstants;
71 import org.apache.archiva.xml.XMLException;
72 import org.apache.commons.io.FilenameUtils;
73 import org.apache.commons.lang.StringUtils;
74 import org.apache.maven.index.context.IndexingContext;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
77 import org.springframework.beans.factory.annotation.Autowired;
78 import org.springframework.stereotype.Service;
79
80 import javax.inject.Inject;
81 import javax.inject.Named;
82 import javax.ws.rs.core.Response;
83 import java.io.IOException;
84 import java.nio.file.*;
85 import java.text.DateFormat;
86 import java.text.SimpleDateFormat;
87 import java.util.*;
88
89 /**
90  * @author Olivier Lamy
91  * @since 1.4-M1
92  */
93 @Service("repositoriesService#rest")
94 public class DefaultRepositoriesService
95     extends AbstractRestService
96     implements RepositoriesService
97 {
98     private Logger log = LoggerFactory.getLogger( getClass() );
99
100     @Inject
101     @Named(value = "taskExecutor#indexing")
102     private ArchivaIndexingTaskExecutor archivaIndexingTaskExecutor;
103
104     @Inject
105     private ManagedRepositoryAdmin managedRepositoryAdmin;
106
107     @Inject
108     private PlexusSisuBridge plexusSisuBridge;
109
110     @Inject
111     private SecuritySystem securitySystem;
112
113     @Inject
114     private RepositoryContentFactory repositoryFactory;
115
116     @Inject
117     @Named(value = "archivaTaskScheduler#repository")
118     private ArchivaTaskScheduler scheduler;
119
120     @Inject
121     private DownloadRemoteIndexScheduler downloadRemoteIndexScheduler;
122
123     @Inject
124     @Named(value = "repositorySessionFactory")
125     protected RepositorySessionFactory repositorySessionFactory;
126
127     @Inject
128     @Autowired(required = false)
129     protected List<RepositoryListener> listeners = new ArrayList<RepositoryListener>();
130
131     @Inject
132     private RepositoryScanner repoScanner;
133
134     /**
135      * Cache used for namespaces
136      */
137     @Inject
138     @Named(value = "cache#namespaces")
139     private Cache<String, Collection<String>> namespacesCache;
140
141     private ChecksumAlgorithm[] algorithms = new ChecksumAlgorithm[]{ ChecksumAlgorithm.SHA1, ChecksumAlgorithm.MD5 };
142
143     @Override
144     public Boolean scanRepository( String repositoryId, boolean fullScan )
145     {
146         return doScanRepository( repositoryId, fullScan );
147     }
148
149     @Override
150     public Boolean alreadyScanning( String repositoryId )
151     {
152         // check queue first to make sure it doesn't get dequeued between calls
153         if ( repositoryTaskScheduler.isProcessingRepositoryTask( repositoryId ) )
154         {
155             return true;
156         }
157         for ( RepositoryScannerInstance scan : repoScanner.getInProgressScans() )
158         {
159             if ( scan.getRepository().getId().equals( repositoryId ) )
160             {
161                 return true;
162             }
163         }
164         return false;
165     }
166
167     @Override
168     public Boolean removeScanningTaskFromQueue( String repositoryId )
169     {
170         RepositoryTask task = new RepositoryTask();
171         task.setRepositoryId( repositoryId );
172         try
173         {
174             return repositoryTaskScheduler.unQueueTask( task );
175         }
176         catch ( TaskQueueException e )
177         {
178             log.error( "failed to unschedule scanning of repo with id {}", repositoryId, e );
179             return false;
180         }
181     }
182
183     @Override
184     public Boolean scanRepositoryNow( String repositoryId, boolean fullScan )
185         throws ArchivaRestServiceException
186     {
187
188         try
189         {
190             ManagedRepository repository = managedRepositoryAdmin.getManagedRepository( repositoryId );
191
192             IndexingContext context = managedRepositoryAdmin.createIndexContext( repository );
193
194             ArtifactIndexingTask task =
195                 new ArtifactIndexingTask( repository, null, ArtifactIndexingTask.Action.FINISH, context );
196
197             task.setExecuteOnEntireRepo( true );
198             task.setOnlyUpdate( !fullScan );
199
200             archivaIndexingTaskExecutor.executeTask( task );
201
202             scheduler.queueTask( new RepositoryTask( repositoryId, fullScan ) );
203
204             return Boolean.TRUE;
205         }
206         catch ( Exception e )
207         {
208             log.error( e.getMessage(), e );
209             throw new ArchivaRestServiceException( e.getMessage(), e );
210         }
211     }
212
213     @Override
214     public Boolean scheduleDownloadRemoteIndex( String repositoryId, boolean now, boolean fullDownload )
215         throws ArchivaRestServiceException
216     {
217         try
218         {
219             downloadRemoteIndexScheduler.scheduleDownloadRemote( repositoryId, now, fullDownload );
220         }
221         catch ( DownloadRemoteIndexException e )
222         {
223             log.error( e.getMessage(), e );
224             throw new ArchivaRestServiceException( e.getMessage(), e );
225         }
226         return Boolean.TRUE;
227     }
228
229     @Override
230     public Boolean copyArtifact( ArtifactTransferRequest artifactTransferRequest )
231         throws ArchivaRestServiceException
232     {
233         // check parameters
234         String userName = getAuditInformation().getUser().getUsername();
235         if ( StringUtils.isBlank( userName ) )
236         {
237             throw new ArchivaRestServiceException( "copyArtifact call: userName not found", null );
238         }
239
240         if ( StringUtils.isBlank( artifactTransferRequest.getRepositoryId() ) )
241         {
242             throw new ArchivaRestServiceException( "copyArtifact call: sourceRepositoryId cannot be null", null );
243         }
244
245         if ( StringUtils.isBlank( artifactTransferRequest.getTargetRepositoryId() ) )
246         {
247             throw new ArchivaRestServiceException( "copyArtifact call: targetRepositoryId cannot be null", null );
248         }
249
250         ManagedRepository source = null;
251         try
252         {
253             source = managedRepositoryAdmin.getManagedRepository( artifactTransferRequest.getRepositoryId() );
254         }
255         catch ( RepositoryAdminException e )
256         {
257             throw new ArchivaRestServiceException( e.getMessage(), e );
258         }
259
260         if ( source == null )
261         {
262             throw new ArchivaRestServiceException(
263                 "cannot find repository with id " + artifactTransferRequest.getRepositoryId(), null );
264         }
265
266         ManagedRepository target = null;
267         try
268         {
269             target = managedRepositoryAdmin.getManagedRepository( artifactTransferRequest.getTargetRepositoryId() );
270         }
271         catch ( RepositoryAdminException e )
272         {
273             throw new ArchivaRestServiceException( e.getMessage(), e );
274         }
275
276         if ( target == null )
277         {
278             throw new ArchivaRestServiceException(
279                 "cannot find repository with id " + artifactTransferRequest.getTargetRepositoryId(), null );
280         }
281
282         if ( StringUtils.isBlank( artifactTransferRequest.getGroupId() ) )
283         {
284             throw new ArchivaRestServiceException( "groupId is mandatory", null );
285         }
286
287         if ( StringUtils.isBlank( artifactTransferRequest.getArtifactId() ) )
288         {
289             throw new ArchivaRestServiceException( "artifactId is mandatory", null );
290         }
291
292         if ( StringUtils.isBlank( artifactTransferRequest.getVersion() ) )
293         {
294             throw new ArchivaRestServiceException( "version is mandatory", null );
295         }
296
297         if ( VersionUtil.isSnapshot( artifactTransferRequest.getVersion() ) )
298         {
299             throw new ArchivaRestServiceException( "copy of SNAPSHOT not supported", null );
300         }
301
302         // end check parameters
303
304         User user = null;
305         try
306         {
307             user = securitySystem.getUserManager().findUser( userName );
308         }
309         catch ( UserNotFoundException e )
310         {
311             throw new ArchivaRestServiceException( "user " + userName + " not found", e );
312         }
313         catch ( UserManagerException e )
314         {
315             throw new ArchivaRestServiceException( "ArchivaRestServiceException:" + e.getMessage(), e );
316         }
317
318         // check karma on source : read
319         AuthenticationResult authn = new AuthenticationResult( true, userName, null );
320         SecuritySession securitySession = new DefaultSecuritySession( authn, user );
321         try
322         {
323             boolean authz =
324                 securitySystem.isAuthorized( securitySession, ArchivaRoleConstants.OPERATION_REPOSITORY_ACCESS,
325                                              artifactTransferRequest.getRepositoryId() );
326             if ( !authz )
327             {
328                 throw new ArchivaRestServiceException(
329                     "not authorized to access repo:" + artifactTransferRequest.getRepositoryId(), null );
330             }
331         }
332         catch ( AuthorizationException e )
333         {
334             log.error( "error reading permission: {}", e.getMessage(), e );
335             throw new ArchivaRestServiceException( e.getMessage(), e );
336         }
337
338         // check karma on target: write
339         try
340         {
341             boolean authz =
342                 securitySystem.isAuthorized( securitySession, ArchivaRoleConstants.OPERATION_REPOSITORY_UPLOAD,
343                                              artifactTransferRequest.getTargetRepositoryId() );
344             if ( !authz )
345             {
346                 throw new ArchivaRestServiceException(
347                     "not authorized to write to repo:" + artifactTransferRequest.getTargetRepositoryId(), null );
348             }
349         }
350         catch ( AuthorizationException e )
351         {
352             log.error( "error reading permission: {}", e.getMessage(), e );
353             throw new ArchivaRestServiceException( e.getMessage(), e );
354         }
355
356         // sounds good we can continue !
357
358         ArtifactReference artifactReference = new ArtifactReference();
359         artifactReference.setArtifactId( artifactTransferRequest.getArtifactId() );
360         artifactReference.setGroupId( artifactTransferRequest.getGroupId() );
361         artifactReference.setVersion( artifactTransferRequest.getVersion() );
362         artifactReference.setClassifier( artifactTransferRequest.getClassifier() );
363         String packaging = StringUtils.trim( artifactTransferRequest.getPackaging() );
364         artifactReference.setType( StringUtils.isEmpty( packaging ) ? "jar" : packaging );
365
366         try
367         {
368
369             ManagedRepositoryContent sourceRepository =
370                 repositoryFactory.getManagedRepositoryContent( artifactTransferRequest.getRepositoryId() );
371
372             String artifactSourcePath = sourceRepository.toPath( artifactReference );
373
374             if ( StringUtils.isEmpty( artifactSourcePath ) )
375             {
376                 log.error( "cannot find artifact {}", artifactTransferRequest );
377                 throw new ArchivaRestServiceException( "cannot find artifact " + artifactTransferRequest.toString(),
378                                                        null );
379             }
380
381             Path artifactFile = Paths.get( source.getLocation(), artifactSourcePath );
382
383             if ( !Files.exists(artifactFile) )
384             {
385                 log.error( "cannot find artifact {}", artifactTransferRequest );
386                 throw new ArchivaRestServiceException( "cannot find artifact " + artifactTransferRequest.toString(),
387                                                        null );
388             }
389
390             ManagedRepositoryContent targetRepository =
391                 repositoryFactory.getManagedRepositoryContent( artifactTransferRequest.getTargetRepositoryId() );
392
393             String artifactPath = targetRepository.toPath( artifactReference );
394
395             int lastIndex = artifactPath.lastIndexOf( '/' );
396
397             String path = artifactPath.substring( 0, lastIndex );
398             Path targetPath = Paths.get( target.getLocation(), path );
399
400             Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
401             int newBuildNumber = 1;
402             String timestamp = null;
403
404             Path versionMetadataFile = targetPath.resolve( MetadataTools.MAVEN_METADATA );
405             /* unused */ getMetadata( versionMetadataFile );
406
407             if ( !Files.exists(targetPath) )
408             {
409                 Files.createDirectories( targetPath );
410             }
411
412             String filename = artifactPath.substring( lastIndex + 1 );
413
414             boolean fixChecksums =
415                 !( archivaAdministration.getKnownContentConsumers().contains( "create-missing-checksums" ) );
416
417             Path targetFile = targetPath.resolve( filename );
418             if ( Files.exists(targetFile) && target.isBlockRedeployments() )
419             {
420                 throw new ArchivaRestServiceException(
421                     "artifact already exists in target repo: " + artifactTransferRequest.getTargetRepositoryId()
422                         + " and redeployment blocked", null
423                 );
424             }
425             else
426             {
427                 copyFile( artifactFile, targetPath, filename, fixChecksums );
428                 queueRepositoryTask( target.getId(), targetFile );
429             }
430
431             // copy source pom to target repo
432             String pomFilename = filename;
433             if ( StringUtils.isNotBlank( artifactTransferRequest.getClassifier() ) )
434             {
435                 pomFilename = StringUtils.remove( pomFilename, "-" + artifactTransferRequest.getClassifier() );
436             }
437             pomFilename = FilenameUtils.removeExtension( pomFilename ) + ".pom";
438
439             Path pomFile = Paths.get(source.getLocation(),
440                 artifactSourcePath.substring( 0, artifactPath.lastIndexOf( '/' ) ) ,
441                 pomFilename );
442
443             if ( pomFile != null && Files.size( pomFile ) > 0 )
444             {
445                 copyFile( pomFile, targetPath, pomFilename, fixChecksums );
446                 queueRepositoryTask( target.getId(), targetPath.resolve( pomFilename ) );
447
448
449             }
450
451             // explicitly update only if metadata-updater consumer is not enabled!
452             if ( !archivaAdministration.getKnownContentConsumers().contains( "metadata-updater" ) )
453             {
454                 updateProjectMetadata( targetPath.toAbsolutePath().toString(), lastUpdatedTimestamp, timestamp, newBuildNumber,
455                                        fixChecksums, artifactTransferRequest );
456
457
458             }
459
460             String msg =
461                 "Artifact \'" + artifactTransferRequest.getGroupId() + ":" + artifactTransferRequest.getArtifactId()
462                     + ":" + artifactTransferRequest.getVersion() + "\' was successfully deployed to repository \'"
463                     + artifactTransferRequest.getTargetRepositoryId() + "\'";
464             log.debug("copyArtifact {}", msg);
465
466         }
467         catch ( RepositoryException e )
468         {
469             log.error( "RepositoryException: {}", e.getMessage(), e );
470             throw new ArchivaRestServiceException( e.getMessage(), e );
471         }
472         catch ( RepositoryAdminException e )
473         {
474             log.error( "RepositoryAdminException: {}", e.getMessage(), e );
475             throw new ArchivaRestServiceException( e.getMessage(), e );
476         }
477         catch ( IOException e )
478         {
479             log.error( "IOException: {}", e.getMessage(), e );
480             throw new ArchivaRestServiceException( e.getMessage(), e );
481         }
482         return true;
483     }
484
485     private void queueRepositoryTask( String repositoryId, Path localFile )
486     {
487         RepositoryTask task = new RepositoryTask();
488         task.setRepositoryId( repositoryId );
489         task.setResourceFile( localFile );
490         task.setUpdateRelatedArtifacts( true );
491         //task.setScanAll( true );
492
493         try
494         {
495             scheduler.queueTask( task );
496         }
497         catch ( TaskQueueException e )
498         {
499             log.error( "Unable to queue repository task to execute consumers on resource file ['{}"
500                            + "'].", localFile.getFileName());
501         }
502     }
503
504     private ArchivaRepositoryMetadata getMetadata( Path metadataFile )
505         throws RepositoryMetadataException
506     {
507         ArchivaRepositoryMetadata metadata = new ArchivaRepositoryMetadata();
508         if ( Files.exists(metadataFile) )
509         {
510             try
511             {
512                 metadata = MavenMetadataReader.read( metadataFile );
513             }
514             catch ( XMLException e )
515             {
516                 throw new RepositoryMetadataException( e.getMessage(), e );
517             }
518         }
519         return metadata;
520     }
521
522     private Path getMetadata( String targetPath )
523     {
524         String artifactPath = targetPath.substring( 0, targetPath.lastIndexOf( FileSystems.getDefault().getSeparator() ));
525
526         return Paths.get( artifactPath, MetadataTools.MAVEN_METADATA );
527     }
528
529     private void copyFile( Path sourceFile, Path targetPath, String targetFilename, boolean fixChecksums )
530         throws IOException
531     {
532         Files.copy( sourceFile, targetPath.resolve( targetFilename ), StandardCopyOption.REPLACE_EXISTING,
533                     StandardCopyOption.COPY_ATTRIBUTES );
534
535         if ( fixChecksums )
536         {
537             fixChecksums( targetPath.resolve( targetFilename ) );
538         }
539     }
540
541     private void fixChecksums( Path file )
542     {
543         ChecksummedFile checksum = new ChecksummedFile( file );
544         checksum.fixChecksums( algorithms );
545     }
546
547     private void updateProjectMetadata( String targetPath, Date lastUpdatedTimestamp, String timestamp, int buildNumber,
548                                         boolean fixChecksums, ArtifactTransferRequest artifactTransferRequest )
549         throws RepositoryMetadataException
550     {
551         List<String> availableVersions = new ArrayList<>();
552         String latestVersion = artifactTransferRequest.getVersion();
553
554         Path projectDir = Paths.get( targetPath ).getParent();
555         Path projectMetadataFile = projectDir.resolve( MetadataTools.MAVEN_METADATA );
556
557         ArchivaRepositoryMetadata projectMetadata = getMetadata( projectMetadataFile );
558
559         if ( Files.exists(projectMetadataFile) )
560         {
561             availableVersions = projectMetadata.getAvailableVersions();
562
563             Collections.sort( availableVersions, VersionComparator.getInstance() );
564
565             if ( !availableVersions.contains( artifactTransferRequest.getVersion() ) )
566             {
567                 availableVersions.add( artifactTransferRequest.getVersion() );
568             }
569
570             latestVersion = availableVersions.get( availableVersions.size() - 1 );
571         }
572         else
573         {
574             availableVersions.add( artifactTransferRequest.getVersion() );
575
576             projectMetadata.setGroupId( artifactTransferRequest.getGroupId() );
577             projectMetadata.setArtifactId( artifactTransferRequest.getArtifactId() );
578         }
579
580         if ( projectMetadata.getGroupId() == null )
581         {
582             projectMetadata.setGroupId( artifactTransferRequest.getGroupId() );
583         }
584
585         if ( projectMetadata.getArtifactId() == null )
586         {
587             projectMetadata.setArtifactId( artifactTransferRequest.getArtifactId() );
588         }
589
590         projectMetadata.setLatestVersion( latestVersion );
591         projectMetadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
592         projectMetadata.setAvailableVersions( availableVersions );
593
594         if ( !VersionUtil.isSnapshot( artifactTransferRequest.getVersion() ) )
595         {
596             projectMetadata.setReleasedVersion( latestVersion );
597         }
598
599         RepositoryMetadataWriter.write( projectMetadata, projectMetadataFile);
600
601         if ( fixChecksums )
602         {
603             fixChecksums( projectMetadataFile );
604         }
605     }
606
607     @Override
608     public Boolean removeProjectVersion( String repositoryId, String namespace, String projectId, String version )
609         throws ArchivaRestServiceException
610     {
611         // if not a generic we can use the standard way to delete artifact
612         if ( !VersionUtil.isGenericSnapshot( version ) )
613         {
614             Artifact artifact = new Artifact( namespace, projectId, version );
615             artifact.setRepositoryId( repositoryId );
616             artifact.setContext( repositoryId );
617             return deleteArtifact( artifact );
618         }
619
620         if ( StringUtils.isEmpty( repositoryId ) )
621         {
622             throw new ArchivaRestServiceException( "repositoryId cannot be null", 400, null );
623         }
624
625         if ( !isAuthorizedToDeleteArtifacts( repositoryId ) )
626         {
627             throw new ArchivaRestServiceException( "not authorized to delete artifacts", 403, null );
628         }
629
630         if ( StringUtils.isEmpty( namespace ) )
631         {
632             throw new ArchivaRestServiceException( "groupId cannot be null", 400, null );
633         }
634
635         if ( StringUtils.isEmpty( projectId ) )
636         {
637             throw new ArchivaRestServiceException( "artifactId cannot be null", 400, null );
638         }
639
640         if ( StringUtils.isEmpty( version ) )
641         {
642             throw new ArchivaRestServiceException( "version cannot be null", 400, null );
643         }
644
645         RepositorySession repositorySession = repositorySessionFactory.createSession();
646
647         try
648         {
649             ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
650
651             VersionedReference ref = new VersionedReference();
652             ref.setArtifactId( projectId );
653             ref.setGroupId( namespace );
654             ref.setVersion( version );
655
656             repository.deleteVersion( ref );
657
658             /*
659             ProjectReference projectReference = new ProjectReference();
660             projectReference.setGroupId( namespace );
661             projectReference.setArtifactId( projectId );
662
663             repository.getVersions(  )
664             */
665
666             ArtifactReference artifactReference = new ArtifactReference();
667             artifactReference.setGroupId( namespace );
668             artifactReference.setArtifactId( projectId );
669             artifactReference.setVersion( version );
670
671             MetadataRepository metadataRepository = repositorySession.getRepository();
672
673             Set<ArtifactReference> related = repository.getRelatedArtifacts( artifactReference );
674             log.debug( "related: {}", related );
675             for ( ArtifactReference artifactRef : related )
676             {
677                 repository.deleteArtifact( artifactRef );
678             }
679
680             Collection<ArtifactMetadata> artifacts =
681                 metadataRepository.getArtifacts( repositoryId, namespace, projectId, version );
682
683             for ( ArtifactMetadata artifactMetadata : artifacts )
684             {
685                 metadataRepository.removeArtifact( artifactMetadata, version );
686             }
687
688             metadataRepository.removeProjectVersion( repositoryId, namespace, projectId, version );
689         }
690         catch ( MetadataRepositoryException e )
691         {
692             throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e );
693         }
694         catch ( MetadataResolutionException e )
695         {
696             throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e );
697         }
698         catch ( RepositoryException e )
699         {
700             throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e );
701         }
702         finally
703         {
704
705             repositorySession.save();
706
707             repositorySession.close();
708         }
709
710         return Boolean.TRUE;
711     }
712
713     @Override
714     public Boolean deleteArtifact( Artifact artifact )
715         throws ArchivaRestServiceException
716     {
717
718         String repositoryId = artifact.getContext();
719         // some rest call can use context or repositoryId
720         // so try both!!
721         if ( StringUtils.isEmpty( repositoryId ) )
722         {
723             repositoryId = artifact.getRepositoryId();
724         }
725         if ( StringUtils.isEmpty( repositoryId ) )
726         {
727             throw new ArchivaRestServiceException( "repositoryId cannot be null", 400, null );
728         }
729
730         if ( !isAuthorizedToDeleteArtifacts( repositoryId ) )
731         {
732             throw new ArchivaRestServiceException( "not authorized to delete artifacts", 403, null );
733         }
734
735         if ( artifact == null )
736         {
737             throw new ArchivaRestServiceException( "artifact cannot be null", 400, null );
738         }
739
740         if ( StringUtils.isEmpty( artifact.getGroupId() ) )
741         {
742             throw new ArchivaRestServiceException( "artifact.groupId cannot be null", 400, null );
743         }
744
745         if ( StringUtils.isEmpty( artifact.getArtifactId() ) )
746         {
747             throw new ArchivaRestServiceException( "artifact.artifactId cannot be null", 400, null );
748         }
749
750         // TODO more control on artifact fields
751
752         boolean snapshotVersion =
753             VersionUtil.isSnapshot( artifact.getVersion() ) | VersionUtil.isGenericSnapshot( artifact.getVersion() );
754
755         RepositorySession repositorySession = repositorySessionFactory.createSession();
756         try
757         {
758             Date lastUpdatedTimestamp = Calendar.getInstance().getTime();
759
760             TimeZone timezone = TimeZone.getTimeZone( "UTC" );
761             DateFormat fmt = new SimpleDateFormat( "yyyyMMdd.HHmmss" );
762             fmt.setTimeZone( timezone );
763             ManagedRepository repoConfig = managedRepositoryAdmin.getManagedRepository( repositoryId );
764
765             VersionedReference ref = new VersionedReference();
766             ref.setArtifactId( artifact.getArtifactId() );
767             ref.setGroupId( artifact.getGroupId() );
768             ref.setVersion( artifact.getVersion() );
769
770             ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
771
772             ArtifactReference artifactReference = new ArtifactReference();
773             artifactReference.setArtifactId( artifact.getArtifactId() );
774             artifactReference.setGroupId( artifact.getGroupId() );
775             artifactReference.setVersion( artifact.getVersion() );
776             artifactReference.setClassifier( artifact.getClassifier() );
777             artifactReference.setType( artifact.getPackaging() );
778
779             MetadataRepository metadataRepository = repositorySession.getRepository();
780
781             String path = repository.toMetadataPath( ref );
782
783             if ( StringUtils.isNotBlank( artifact.getClassifier() ) )
784             {
785                 if ( StringUtils.isBlank( artifact.getPackaging() ) )
786                 {
787                     throw new ArchivaRestServiceException( "You must configure a type/packaging when using classifier",
788                                                            400, null );
789                 }
790
791                 repository.deleteArtifact( artifactReference );
792
793             }
794             else
795             {
796
797                 int index = path.lastIndexOf( '/' );
798                 path = path.substring( 0, index );
799                 Path targetPath = Paths.get( repoConfig.getLocation(), path );
800
801                 if ( !Files.exists(targetPath) )
802                 {
803                     //throw new ContentNotFoundException(
804                     //    artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion() );
805                     log.warn( "targetPath {} not found skip file deletion", targetPath );
806                 }
807
808                 // TODO: this should be in the storage mechanism so that it is all tied together
809                 // delete from file system
810                 if ( !snapshotVersion )
811                 {
812                     repository.deleteVersion( ref );
813                 }
814                 else
815                 {
816                     Set<ArtifactReference> related = repository.getRelatedArtifacts( artifactReference );
817                     log.debug( "related: {}", related );
818                     for ( ArtifactReference artifactRef : related )
819                     {
820                         repository.deleteArtifact( artifactRef );
821                     }
822                 }
823                 Path metadataFile = getMetadata( targetPath.toAbsolutePath().toString() );
824                 ArchivaRepositoryMetadata metadata = getMetadata( metadataFile );
825
826                 updateMetadata( metadata, metadataFile, lastUpdatedTimestamp, artifact );
827             }
828             Collection<ArtifactMetadata> artifacts = Collections.emptyList();
829
830             if ( snapshotVersion )
831             {
832                 String baseVersion = VersionUtil.getBaseVersion( artifact.getVersion() );
833                 artifacts =
834                     metadataRepository.getArtifacts( repositoryId, artifact.getGroupId(), artifact.getArtifactId(),
835                                                      baseVersion );
836             }
837             else
838             {
839                 artifacts =
840                     metadataRepository.getArtifacts( repositoryId, artifact.getGroupId(), artifact.getArtifactId(),
841                                                      artifact.getVersion() );
842             }
843
844             log.debug( "artifacts: {}", artifacts );
845
846             if ( artifacts.isEmpty() )
847             {
848                 if ( !snapshotVersion )
849                 {
850                     // verify metata repository doesn't contains anymore the version
851                     Collection<String> projectVersions =
852                         metadataRepository.getProjectVersions( repositoryId, artifact.getGroupId(),
853                                                                artifact.getArtifactId() );
854
855                     if ( projectVersions.contains( artifact.getVersion() ) )
856                     {
857                         log.warn( "artifact not found when deleted but version still here ! so force cleanup" );
858                         metadataRepository.removeProjectVersion( repositoryId, artifact.getGroupId(),
859                                                                  artifact.getArtifactId(), artifact.getVersion() );
860                     }
861
862                 }
863             }
864
865             for ( ArtifactMetadata artifactMetadata : artifacts )
866             {
867
868                 // TODO: mismatch between artifact (snapshot) version and project (base) version here
869                 if ( artifactMetadata.getVersion().equals( artifact.getVersion() ) )
870                 {
871                     if ( StringUtils.isNotBlank( artifact.getClassifier() ) )
872                     {
873                         if ( StringUtils.isBlank( artifact.getPackaging() ) )
874                         {
875                             throw new ArchivaRestServiceException(
876                                 "You must configure a type/packaging when using classifier", 400, null );
877                         }
878                         // cleanup facet which contains classifier information
879                         MavenArtifactFacet mavenArtifactFacet =
880                             (MavenArtifactFacet) artifactMetadata.getFacet( MavenArtifactFacet.FACET_ID );
881
882                         if ( StringUtils.equals( artifact.getClassifier(), mavenArtifactFacet.getClassifier() ) )
883                         {
884                             artifactMetadata.removeFacet( MavenArtifactFacet.FACET_ID );
885                             String groupId = artifact.getGroupId(), artifactId = artifact.getArtifactId(), version =
886                                 artifact.getVersion();
887                             MavenArtifactFacet mavenArtifactFacetToCompare = new MavenArtifactFacet();
888                             mavenArtifactFacetToCompare.setClassifier( artifact.getClassifier() );
889                             metadataRepository.removeArtifact( repositoryId, groupId, artifactId, version,
890                                                                mavenArtifactFacetToCompare );
891                             metadataRepository.save();
892                         }
893
894                     }
895                     else
896                     {
897                         if ( snapshotVersion )
898                         {
899                             metadataRepository.removeArtifact( artifactMetadata,
900                                                                VersionUtil.getBaseVersion( artifact.getVersion() ) );
901                         }
902                         else
903                         {
904                             metadataRepository.removeArtifact( artifactMetadata.getRepositoryId(),
905                                                                artifactMetadata.getNamespace(),
906                                                                artifactMetadata.getProject(), artifact.getVersion(),
907                                                                artifactMetadata.getId() );
908                         }
909                     }
910                     // TODO: move into the metadata repository proper - need to differentiate attachment of
911                     //       repository metadata to an artifact
912                     for ( RepositoryListener listener : listeners )
913                     {
914                         listener.deleteArtifact( metadataRepository, repository.getId(),
915                                                  artifactMetadata.getNamespace(), artifactMetadata.getProject(),
916                                                  artifactMetadata.getVersion(), artifactMetadata.getId() );
917                     }
918
919                     triggerAuditEvent( repositoryId, path, AuditEvent.REMOVE_FILE );
920                 }
921             }
922         }
923         catch ( ContentNotFoundException e )
924         {
925             throw new ArchivaRestServiceException( "Artifact does not exist: " + e.getMessage(), 400, e );
926         }
927         catch ( RepositoryNotFoundException e )
928         {
929             throw new ArchivaRestServiceException( "Target repository cannot be found: " + e.getMessage(), 400, e );
930         }
931         catch ( RepositoryException e )
932         {
933             throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e );
934         }
935         catch ( MetadataResolutionException e )
936         {
937             throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e );
938         }
939         catch ( MetadataRepositoryException e )
940         {
941             throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e );
942         }
943         catch ( RepositoryAdminException e )
944         {
945             throw new ArchivaRestServiceException( "RepositoryAdmin exception: " + e.getMessage(), 500, e );
946         }
947         finally
948         {
949
950             repositorySession.save();
951
952             repositorySession.close();
953         }
954         return Boolean.TRUE;
955     }
956
957     @Override
958     public Boolean deleteGroupId( String groupId, String repositoryId )
959         throws ArchivaRestServiceException
960     {
961         if ( StringUtils.isEmpty( repositoryId ) )
962         {
963             throw new ArchivaRestServiceException( "repositoryId cannot be null", 400, null );
964         }
965
966         if ( !isAuthorizedToDeleteArtifacts( repositoryId ) )
967         {
968             throw new ArchivaRestServiceException( "not authorized to delete artifacts", 403, null );
969         }
970
971         if ( StringUtils.isEmpty( groupId ) )
972         {
973             throw new ArchivaRestServiceException( "groupId cannot be null", 400, null );
974         }
975
976         RepositorySession repositorySession = repositorySessionFactory.createSession();
977
978         try
979         {
980             ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
981
982             repository.deleteGroupId( groupId );
983
984             MetadataRepository metadataRepository = repositorySession.getRepository();
985
986             metadataRepository.removeNamespace( repositoryId, groupId );
987
988             // just invalidate cache entry
989             String cacheKey = repositoryId + "-" + groupId;
990             namespacesCache.remove( cacheKey );
991             namespacesCache.remove( repositoryId );
992
993             metadataRepository.save();
994         }
995         catch ( MetadataRepositoryException e )
996         {
997             log.error( e.getMessage(), e );
998             throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e );
999         }
1000         catch ( RepositoryException e )
1001         {
1002             log.error( e.getMessage(), e );
1003             throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e );
1004         }
1005         finally
1006         {
1007
1008             repositorySession.close();
1009         }
1010         return true;
1011     }
1012
1013     @Override
1014     public Boolean deleteProject( String groupId, String projectId, String repositoryId )
1015         throws ArchivaRestServiceException
1016     {
1017         if ( StringUtils.isEmpty( repositoryId ) )
1018         {
1019             throw new ArchivaRestServiceException( "repositoryId cannot be null", 400, null );
1020         }
1021
1022         if ( !isAuthorizedToDeleteArtifacts( repositoryId ) )
1023         {
1024             throw new ArchivaRestServiceException( "not authorized to delete artifacts", 403, null );
1025         }
1026
1027         if ( StringUtils.isEmpty( groupId ) )
1028         {
1029             throw new ArchivaRestServiceException( "groupId cannot be null", 400, null );
1030         }
1031
1032         if ( StringUtils.isEmpty( projectId ) )
1033         {
1034             throw new ArchivaRestServiceException( "artifactId cannot be null", 400, null );
1035         }
1036
1037         RepositorySession repositorySession = repositorySessionFactory.createSession();
1038
1039         try
1040         {
1041             ManagedRepositoryContent repository = repositoryFactory.getManagedRepositoryContent( repositoryId );
1042
1043             repository.deleteProject( groupId, projectId );
1044         }
1045         catch ( ContentNotFoundException e )
1046         {
1047             log.warn( "skip ContentNotFoundException: {}", e.getMessage() );
1048         }
1049         catch ( RepositoryException e )
1050         {
1051             log.error( e.getMessage(), e );
1052             throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e );
1053         }
1054
1055         try
1056         {
1057
1058             MetadataRepository metadataRepository = repositorySession.getRepository();
1059
1060             metadataRepository.removeProject( repositoryId, groupId, projectId );
1061
1062             metadataRepository.save();
1063         }
1064         catch ( MetadataRepositoryException e )
1065         {
1066             log.error( e.getMessage(), e );
1067             throw new ArchivaRestServiceException( "Repository exception: " + e.getMessage(), 500, e );
1068         }
1069         finally
1070         {
1071
1072             repositorySession.close();
1073         }
1074         return true;
1075
1076     }
1077
1078     @Override
1079     public Boolean isAuthorizedToDeleteArtifacts( String repoId )
1080         throws ArchivaRestServiceException
1081     {
1082         String userName =
1083             getAuditInformation().getUser() == null ? "guest" : getAuditInformation().getUser().getUsername();
1084
1085         try
1086         {
1087             return userRepositories.isAuthorizedToDeleteArtifacts( userName, repoId );
1088         }
1089         catch ( ArchivaSecurityException e )
1090         {
1091             throw new ArchivaRestServiceException( e.getMessage(),
1092                                                    Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(), e );
1093         }
1094     }
1095
1096     @Override
1097     public RepositoryScanStatistics scanRepositoryDirectoriesNow( String repositoryId )
1098         throws ArchivaRestServiceException
1099     {
1100         long sinceWhen = RepositoryScanner.FRESH_SCAN;
1101         try
1102         {
1103             return repoScanner.scan( getManagedRepositoryAdmin().getManagedRepository( repositoryId ), sinceWhen );
1104         }
1105         catch ( RepositoryScannerException e )
1106         {
1107             log.error( e.getMessage(), e );
1108             throw new ArchivaRestServiceException( "RepositoryScannerException exception: " + e.getMessage(), 500, e );
1109         }
1110         catch ( RepositoryAdminException e )
1111         {
1112             log.error( e.getMessage(), e );
1113             throw new ArchivaRestServiceException( "RepositoryScannerException exception: " + e.getMessage(), 500, e );
1114         }
1115     }
1116
1117     /**
1118      * Update artifact level metadata. Creates one if metadata does not exist after artifact deletion.
1119      *
1120      * @param metadata
1121      */
1122     private void updateMetadata( ArchivaRepositoryMetadata metadata, Path metadataFile, Date lastUpdatedTimestamp,
1123                                  Artifact artifact )
1124         throws RepositoryMetadataException
1125     {
1126         List<String> availableVersions = new ArrayList<>();
1127         String latestVersion = "";
1128
1129         if ( Files.exists(metadataFile) )
1130         {
1131             if ( metadata.getAvailableVersions() != null )
1132             {
1133                 availableVersions = metadata.getAvailableVersions();
1134
1135                 if ( availableVersions.size() > 0 )
1136                 {
1137                     Collections.sort( availableVersions, VersionComparator.getInstance() );
1138
1139                     if ( availableVersions.contains( artifact.getVersion() ) )
1140                     {
1141                         availableVersions.remove( availableVersions.indexOf( artifact.getVersion() ) );
1142                     }
1143                     if ( availableVersions.size() > 0 )
1144                     {
1145                         latestVersion = availableVersions.get( availableVersions.size() - 1 );
1146                     }
1147                 }
1148             }
1149         }
1150
1151         if ( metadata.getGroupId() == null )
1152         {
1153             metadata.setGroupId( artifact.getGroupId() );
1154         }
1155         if ( metadata.getArtifactId() == null )
1156         {
1157             metadata.setArtifactId( artifact.getArtifactId() );
1158         }
1159
1160         if ( !VersionUtil.isSnapshot( artifact.getVersion() ) )
1161         {
1162             if ( metadata.getReleasedVersion() != null && metadata.getReleasedVersion().equals(
1163                 artifact.getVersion() ) )
1164             {
1165                 metadata.setReleasedVersion( latestVersion );
1166             }
1167         }
1168
1169         metadata.setLatestVersion( latestVersion );
1170         metadata.setLastUpdatedTimestamp( lastUpdatedTimestamp );
1171         metadata.setAvailableVersions( availableVersions );
1172
1173         RepositoryMetadataWriter.write( metadata, metadataFile);
1174         ChecksummedFile checksum = new ChecksummedFile( metadataFile );
1175         checksum.fixChecksums( algorithms );
1176     }
1177
1178     @Override
1179     public StringList getRunningRemoteDownloadIds()
1180     {
1181         return new StringList( downloadRemoteIndexScheduler.getRunningRemoteDownloadIds() );
1182     }
1183
1184     public ManagedRepositoryAdmin getManagedRepositoryAdmin()
1185     {
1186         return managedRepositoryAdmin;
1187     }
1188
1189     public void setManagedRepositoryAdmin( ManagedRepositoryAdmin managedRepositoryAdmin )
1190     {
1191         this.managedRepositoryAdmin = managedRepositoryAdmin;
1192     }
1193
1194     public RepositoryContentFactory getRepositoryFactory()
1195     {
1196         return repositoryFactory;
1197     }
1198
1199     public void setRepositoryFactory( RepositoryContentFactory repositoryFactory )
1200     {
1201         this.repositoryFactory = repositoryFactory;
1202     }
1203
1204     public RepositorySessionFactory getRepositorySessionFactory()
1205     {
1206         return repositorySessionFactory;
1207     }
1208
1209     public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory )
1210     {
1211         this.repositorySessionFactory = repositorySessionFactory;
1212     }
1213
1214     public List<RepositoryListener> getListeners()
1215     {
1216         return listeners;
1217     }
1218
1219     public void setListeners( List<RepositoryListener> listeners )
1220     {
1221         this.listeners = listeners;
1222     }
1223
1224     public ArchivaAdministration getArchivaAdministration()
1225     {
1226         return archivaAdministration;
1227     }
1228
1229     public void setArchivaAdministration( ArchivaAdministration archivaAdministration )
1230     {
1231         this.archivaAdministration = archivaAdministration;
1232     }
1233 }
1234
1235