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