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