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