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