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