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