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