1 package org.apache.archiva.admin.repository.managed;
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
21 import org.apache.archiva.admin.model.AuditInformation;
22 import org.apache.archiva.admin.model.RepositoryAdminException;
23 import org.apache.archiva.admin.model.beans.ManagedRepository;
24 import org.apache.archiva.admin.model.managed.ManagedRepositoryAdmin;
25 import org.apache.archiva.admin.repository.AbstractRepositoryAdmin;
26 import org.apache.archiva.audit.AuditEvent;
27 import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
28 import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
29 import org.apache.archiva.metadata.repository.MetadataRepository;
30 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
31 import org.apache.archiva.metadata.repository.RepositorySession;
32 import org.apache.archiva.metadata.repository.RepositorySessionFactory;
33 import org.apache.archiva.metadata.repository.stats.RepositoryStatisticsManager;
34 import org.apache.archiva.scheduler.repository.RepositoryArchivaTaskScheduler;
35 import org.apache.archiva.scheduler.repository.RepositoryTask;
36 import org.apache.archiva.security.common.ArchivaRoleConstants;
37 import org.apache.commons.io.FileUtils;
38 import org.apache.commons.lang.StringUtils;
39 import org.apache.commons.validator.GenericValidator;
40 import org.apache.archiva.configuration.Configuration;
41 import org.apache.archiva.configuration.ManagedRepositoryConfiguration;
42 import org.apache.archiva.configuration.ProxyConnectorConfiguration;
43 import org.apache.archiva.configuration.RepositoryGroupConfiguration;
44 import org.apache.maven.index.NexusIndexer;
45 import org.apache.maven.index.context.IndexingContext;
46 import org.codehaus.plexus.redback.role.RoleManager;
47 import org.codehaus.plexus.redback.role.RoleManagerException;
48 import org.codehaus.plexus.taskqueue.TaskQueueException;
49 import org.codehaus.redback.components.scheduler.CronExpressionValidator;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52 import org.springframework.stereotype.Service;
54 import javax.inject.Inject;
55 import javax.inject.Named;
57 import java.io.IOException;
58 import java.util.ArrayList;
59 import java.util.Arrays;
60 import java.util.HashMap;
61 import java.util.List;
65 * FIXME remove all generic Exception to have usefull ones
66 * FIXME review the staging mechanism to have a per user session one
68 * @author Olivier Lamy
70 @Service( "managedRepositoryAdmin#default" )
71 public class DefaultManagedRepositoryAdmin
72 extends AbstractRepositoryAdmin
73 implements ManagedRepositoryAdmin
76 public static final String REPOSITORY_LOCATION_VALID_EXPRESSION = "^[-a-zA-Z0-9._/~:?!&=\\\\]+$";
78 private Logger log = LoggerFactory.getLogger( getClass() );
80 public static final String STAGE_REPO_ID_END = "-stage";
84 @Named( value = "archivaTaskScheduler#repository" )
85 private RepositoryArchivaTaskScheduler repositoryTaskScheduler;
88 private RepositorySessionFactory repositorySessionFactory;
91 private RepositoryStatisticsManager repositoryStatisticsManager;
94 private PlexusSisuBridge plexusSisuBridge;
98 protected RoleManager roleManager;
100 public List<ManagedRepository> getManagedRepositories()
101 throws RepositoryAdminException
103 List<ManagedRepositoryConfiguration> managedRepoConfigs =
104 getArchivaConfiguration().getConfiguration().getManagedRepositories();
106 List<ManagedRepository> managedRepos = new ArrayList<ManagedRepository>( managedRepoConfigs.size() );
108 for ( ManagedRepositoryConfiguration repoConfig : managedRepoConfigs )
110 // TODO add staging repo information back too
111 ManagedRepository repo =
112 new ManagedRepository( repoConfig.getId(), repoConfig.getName(), repoConfig.getLocation(),
113 repoConfig.getLayout(), repoConfig.isSnapshots(), repoConfig.isReleases(),
114 repoConfig.isBlockRedeployments(), repoConfig.getRefreshCronExpression(),
115 repoConfig.getIndexDir(), repoConfig.isScanned(), repoConfig.getDaysOlder(),
116 repoConfig.getRetentionCount(), repoConfig.isDeleteReleasedSnapshots(), false );
118 managedRepos.add( repo );
124 public Map<String, ManagedRepository> getManagedRepositoriesAsMap()
125 throws RepositoryAdminException
127 List<ManagedRepository> managedRepositories = getManagedRepositories();
128 Map<String, ManagedRepository> repositoriesMap =
129 new HashMap<String, ManagedRepository>( managedRepositories.size() );
130 for ( ManagedRepository managedRepository : managedRepositories )
132 repositoriesMap.put( managedRepository.getId(), managedRepository );
134 return repositoriesMap;
137 public ManagedRepository getManagedRepository( String repositoryId )
138 throws RepositoryAdminException
140 List<ManagedRepository> repos = getManagedRepositories();
141 for ( ManagedRepository repo : repos )
143 if ( StringUtils.equals( repo.getId(), repositoryId ) )
151 public Boolean addManagedRepository( ManagedRepository managedRepository, boolean needStageRepo,
152 AuditInformation auditInformation )
153 throws RepositoryAdminException
156 getRepositoryCommonValidator().basicValidation( managedRepository, false );
157 triggerAuditEvent( managedRepository.getId(), null, AuditEvent.ADD_MANAGED_REPO, auditInformation );
159 addManagedRepository( managedRepository.getId(), managedRepository.getLayout(), managedRepository.getName(),
160 managedRepository.getLocation(), managedRepository.isBlockRedeployments(),
161 managedRepository.isReleases(), managedRepository.isSnapshots(), needStageRepo,
162 managedRepository.getCronExpression(), managedRepository.getIndexDirectory(),
163 managedRepository.getDaysOlder(), managedRepository.getRetentionCount(),
164 managedRepository.isDeleteReleasedSnapshots(), auditInformation,
165 getArchivaConfiguration().getConfiguration() ) != null;
169 private ManagedRepositoryConfiguration addManagedRepository( String repoId, String layout, String name,
170 String location, boolean blockRedeployments,
171 boolean releasesIncluded, boolean snapshotsIncluded,
172 boolean stageRepoNeeded, String cronExpression,
173 String indexDir, int daysOlder, int retentionCount,
174 boolean deteleReleasedSnapshots,
175 AuditInformation auditInformation,
176 Configuration config )
177 throws RepositoryAdminException
180 // FIXME : olamy can be empty to avoid scheduled scan ?
181 if ( StringUtils.isNotBlank( cronExpression ) )
183 CronExpressionValidator validator = new CronExpressionValidator();
185 if ( !validator.validate( cronExpression ) )
187 throw new RepositoryAdminException( "Invalid cron expression." );
192 throw new RepositoryAdminException( "Cron expression cannot be empty." );
195 String repoLocation = getRepositoryCommonValidator().removeExpressions( location );
197 if ( !GenericValidator.matchRegexp( repoLocation, REPOSITORY_LOCATION_VALID_EXPRESSION ) )
199 throw new RepositoryAdminException(
200 "Invalid repository location. Directory must only contain alphanumeric characters, equals(=), question-marks(?), "
201 + "exclamation-points(!), ampersands(&), forward-slashes(/), back-slashes(\\), underscores(_), dots(.), colons(:), tildes(~), and dashes(-)." );
204 ManagedRepositoryConfiguration repository = new ManagedRepositoryConfiguration();
206 repository.setId( repoId );
207 repository.setBlockRedeployments( blockRedeployments );
208 repository.setReleases( releasesIncluded );
209 repository.setSnapshots( snapshotsIncluded );
210 repository.setName( name );
211 repository.setLocation( repoLocation );
212 repository.setLayout( layout );
213 repository.setRefreshCronExpression( cronExpression );
214 repository.setIndexDir( indexDir );
215 repository.setDaysOlder( daysOlder );
216 repository.setRetentionCount( retentionCount );
217 repository.setDeleteReleasedSnapshots( deteleReleasedSnapshots );
218 repository.setIndexDir( indexDir );
222 addRepository( repository, config );
223 addRepositoryRoles( repository );
225 if ( stageRepoNeeded )
227 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( repository );
228 addRepository( stagingRepository, config );
229 addRepositoryRoles( stagingRepository );
230 triggerAuditEvent( stagingRepository.getId(), null, AuditEvent.ADD_MANAGED_REPO, auditInformation );
233 catch ( RoleManagerException e )
235 throw new RepositoryAdminException( "failed to add repository roles " + e.getMessage(), e );
237 catch ( IOException e )
239 throw new RepositoryAdminException( "failed to add repository " + e.getMessage(), e );
242 saveConfiguration( config );
244 //MRM-1342 Repository statistics report doesn't appear to be working correctly
245 //scan repository when adding of repository is successful
248 scanRepository( repoId, true );
249 // olamy no need of scanning staged repo
251 if ( stageRepoNeeded )
253 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( repository );
254 scanRepository( stagingRepository.getId(), true );
257 catch ( Exception e )
259 log.warn( new StringBuilder( "Unable to scan repository [" ).append( repoId ).append( "]: " ).append(
260 e.getMessage() ).toString(), e );
267 // FIXME cleanup repositoryGroups when deleting a ManagedRepo
268 public Boolean deleteManagedRepository( String repositoryId, AuditInformation auditInformation,
269 boolean deleteContent )
270 throws RepositoryAdminException
272 Configuration config = getArchivaConfiguration().getConfiguration();
274 ManagedRepositoryConfiguration repository = config.findManagedRepositoryById( repositoryId );
276 if ( repository == null )
278 throw new RepositoryAdminException( "A repository with that id does not exist" );
281 triggerAuditEvent( repositoryId, null, AuditEvent.DELETE_MANAGED_REPO, auditInformation );
283 deleteManagedRepository( repository, deleteContent, config, false );
285 // stage repo exists ?
286 ManagedRepositoryConfiguration stagingRepository =
287 getArchivaConfiguration().getConfiguration().findManagedRepositoryById( repositoryId + STAGE_REPO_ID_END );
288 if ( stagingRepository != null )
290 // do not trigger event when deleting the staged one
291 deleteManagedRepository( stagingRepository, deleteContent, config, true );
296 saveConfiguration( config );
298 catch ( Exception e )
300 throw new RepositoryAdminException( "Error saving configuration for delete action" + e.getMessage() );
306 private Boolean deleteManagedRepository( ManagedRepositoryConfiguration repository, boolean deleteContent,
307 Configuration config, boolean stagedOne )
308 throws RepositoryAdminException
313 NexusIndexer nexusIndexer = plexusSisuBridge.lookup( NexusIndexer.class );
315 IndexingContext context = nexusIndexer.getIndexingContexts().get( repository.getId() );
316 if ( context != null )
318 nexusIndexer.removeIndexingContext( context, deleteContent );
321 catch ( PlexusSisuBridgeException e )
323 throw new RepositoryAdminException( e.getMessage(), e );
325 catch ( IOException e )
327 throw new RepositoryAdminException( e.getMessage(), e );
331 RepositorySession repositorySession = getRepositorySessionFactory().createSession();
334 MetadataRepository metadataRepository = repositorySession.getRepository();
335 metadataRepository.removeRepository( repository.getId() );
336 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
337 getRepositoryStatisticsManager().deleteStatistics( metadataRepository, repository.getId() );
338 repositorySession.save();
340 catch ( MetadataRepositoryException e )
342 throw new RepositoryAdminException( e.getMessage(), e );
346 repositorySession.close();
349 config.removeManagedRepository( repository );
353 // TODO could be async ? as directory can be huge
354 File dir = new File( repository.getLocation() );
355 if ( !FileUtils.deleteQuietly( dir ) )
357 throw new RepositoryAdminException( "Cannot delete repository " + dir );
361 // olamy: copy list for reading as a unit test in webapp fail with ConcurrentModificationException
362 List<ProxyConnectorConfiguration> proxyConnectors =
363 new ArrayList<ProxyConnectorConfiguration>( config.getProxyConnectors() );
364 for ( ProxyConnectorConfiguration proxyConnector : proxyConnectors )
366 if ( StringUtils.equals( proxyConnector.getSourceRepoId(), repository.getId() ) )
368 config.removeProxyConnector( proxyConnector );
372 Map<String, List<String>> repoToGroupMap = config.getRepositoryToGroupMap();
373 if ( repoToGroupMap != null )
375 if ( repoToGroupMap.containsKey( repository.getId() ) )
377 List<String> repoGroups = repoToGroupMap.get( repository.getId() );
378 for ( String repoGroup : repoGroups )
380 // copy to prevent UnsupportedOperationException
381 RepositoryGroupConfiguration repositoryGroupConfiguration =
382 config.findRepositoryGroupById( repoGroup );
383 List<String> repos = new ArrayList<String>( repositoryGroupConfiguration.getRepositories() );
384 config.removeRepositoryGroup( repositoryGroupConfiguration );
385 repos.remove( repository.getId() );
386 repositoryGroupConfiguration.setRepositories( repos );
387 config.addRepositoryGroup( repositoryGroupConfiguration );
394 removeRepositoryRoles( repository );
396 catch ( RoleManagerException e )
398 throw new RepositoryAdminException(
399 "fail to remove repository roles for repository " + repository.getId() + " : " + e.getMessage(), e );
402 saveConfiguration( config );
408 public Boolean updateManagedRepository( ManagedRepository managedRepository, boolean needStageRepo,
409 AuditInformation auditInformation, boolean resetStats )
410 throws RepositoryAdminException
413 log.debug( "updateManagedConfiguration repo {} needStage {} resetStats {} ",
414 Arrays.asList( managedRepository, needStageRepo, resetStats ).toArray() );
416 // Ensure that the fields are valid.
418 getRepositoryCommonValidator().basicValidation( managedRepository, true );
420 Configuration configuration = getArchivaConfiguration().getConfiguration();
422 ManagedRepositoryConfiguration toremove = configuration.findManagedRepositoryById( managedRepository.getId() );
424 if ( toremove != null )
426 configuration.removeManagedRepository( toremove );
429 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( toremove );
431 // TODO remove content from old if path has changed !!!!!
433 if ( stagingRepository != null )
435 configuration.removeManagedRepository( stagingRepository );
438 ManagedRepositoryConfiguration managedRepositoryConfiguration =
439 addManagedRepository( managedRepository.getId(), managedRepository.getLayout(), managedRepository.getName(),
440 managedRepository.getLocation(), managedRepository.isBlockRedeployments(),
441 managedRepository.isReleases(), managedRepository.isSnapshots(), needStageRepo,
442 managedRepository.getCronExpression(), managedRepository.getIndexDirectory(),
443 managedRepository.getDaysOlder(), managedRepository.getRetentionCount(),
444 managedRepository.isDeleteReleasedSnapshots(), auditInformation,
445 getArchivaConfiguration().getConfiguration() );
447 // Save the repository configuration.
448 RepositorySession repositorySession = getRepositorySessionFactory().createSession();
452 triggerAuditEvent( managedRepositoryConfiguration.getId(), null, AuditEvent.MODIFY_MANAGED_REPO,
455 saveConfiguration( this.getArchivaConfiguration().getConfiguration() );
458 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
459 getRepositoryStatisticsManager().deleteStatistics( repositorySession.getRepository(),
460 managedRepositoryConfiguration.getId() );
461 repositorySession.save();
465 catch ( MetadataRepositoryException e )
467 throw new RepositoryAdminException( e.getMessage(), e );
471 repositorySession.close();
477 //--------------------------
479 //--------------------------
482 protected void addRepository( ManagedRepositoryConfiguration repository, Configuration configuration )
483 throws RepositoryAdminException, IOException
485 // Normalize the path
486 File file = new File( repository.getLocation() );
487 repository.setLocation( file.getCanonicalPath() );
488 if ( !file.exists() )
492 if ( !file.exists() || !file.isDirectory() )
494 throw new RepositoryAdminException(
495 "Unable to add repository - no write access, can not create the root directory: " + file );
498 configuration.addManagedRepository( repository );
501 private ManagedRepositoryConfiguration getStageRepoConfig( ManagedRepositoryConfiguration repository )
503 ManagedRepositoryConfiguration stagingRepository = new ManagedRepositoryConfiguration();
504 stagingRepository.setId( repository.getId() + STAGE_REPO_ID_END );
505 stagingRepository.setLayout( repository.getLayout() );
506 stagingRepository.setName( repository.getName() + STAGE_REPO_ID_END );
507 stagingRepository.setBlockRedeployments( repository.isBlockRedeployments() );
508 stagingRepository.setDaysOlder( repository.getDaysOlder() );
509 stagingRepository.setDeleteReleasedSnapshots( repository.isDeleteReleasedSnapshots() );
510 stagingRepository.setIndexDir( repository.getIndexDir() );
511 String path = repository.getLocation();
512 int lastIndex = path.lastIndexOf( '/' );
513 stagingRepository.setLocation( path.substring( 0, lastIndex ) + "/" + stagingRepository.getId() );
514 stagingRepository.setRefreshCronExpression( repository.getRefreshCronExpression() );
515 stagingRepository.setReleases( repository.isReleases() );
516 stagingRepository.setRetentionCount( repository.getRetentionCount() );
517 stagingRepository.setScanned( repository.isScanned() );
518 stagingRepository.setSnapshots( repository.isSnapshots() );
519 return stagingRepository;
522 public Boolean scanRepository( String repositoryId, boolean fullScan )
524 if ( getRepositoryTaskScheduler().isProcessingRepositoryTask( repositoryId ) )
526 log.info( "scanning of repository with id {} already scheduled", repositoryId );
528 RepositoryTask task = new RepositoryTask();
529 task.setRepositoryId( repositoryId );
530 task.setScanAll( fullScan );
533 getRepositoryTaskScheduler().queueTask( task );
535 catch ( TaskQueueException e )
537 log.error( "failed to schedule scanning of repo with id {}", repositoryId, e );
543 protected void addRepositoryRoles( ManagedRepositoryConfiguration newRepository )
544 throws RoleManagerException
546 String repoId = newRepository.getId();
548 // TODO: double check these are configured on start up
549 // TODO: belongs in the business logic
551 if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
553 getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
556 if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
558 getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
562 protected void removeRepositoryRoles( ManagedRepositoryConfiguration existingRepository )
563 throws RoleManagerException
565 String repoId = existingRepository.getId();
567 if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
569 getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
572 if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
574 getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
577 log.debug( "removed user roles associated with repository {}", repoId );
580 //--------------------------
582 //--------------------------
585 public RoleManager getRoleManager()
590 public void setRoleManager( RoleManager roleManager )
592 this.roleManager = roleManager;
595 public RepositoryStatisticsManager getRepositoryStatisticsManager()
597 return repositoryStatisticsManager;
600 public void setRepositoryStatisticsManager( RepositoryStatisticsManager repositoryStatisticsManager )
602 this.repositoryStatisticsManager = repositoryStatisticsManager;
605 public RepositorySessionFactory getRepositorySessionFactory()
607 return repositorySessionFactory;
610 public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory )
612 this.repositorySessionFactory = repositorySessionFactory;
616 public RepositoryArchivaTaskScheduler getRepositoryTaskScheduler()
618 return repositoryTaskScheduler;
621 public void setRepositoryTaskScheduler( RepositoryArchivaTaskScheduler repositoryTaskScheduler )
623 this.repositoryTaskScheduler = repositoryTaskScheduler;