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