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