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