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