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