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.managed.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.metadata.repository.MetadataRepository;
28 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
29 import org.apache.archiva.metadata.repository.RepositorySession;
30 import org.apache.archiva.metadata.repository.RepositorySessionFactory;
31 import org.apache.archiva.metadata.repository.stats.RepositoryStatisticsManager;
32 import org.apache.archiva.scheduler.repository.RepositoryArchivaTaskScheduler;
33 import org.apache.archiva.scheduler.repository.RepositoryTask;
34 import org.apache.archiva.security.common.ArchivaRoleConstants;
35 import org.apache.commons.io.FileUtils;
36 import org.apache.commons.lang.StringUtils;
37 import org.apache.commons.validator.GenericValidator;
38 import org.apache.maven.archiva.configuration.Configuration;
39 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
40 import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration;
41 import org.apache.maven.archiva.configuration.RepositoryGroupConfiguration;
42 import org.codehaus.plexus.redback.role.RoleManager;
43 import org.codehaus.plexus.redback.role.RoleManagerException;
44 import org.codehaus.plexus.taskqueue.TaskQueueException;
45 import org.codehaus.redback.components.scheduler.CronExpressionValidator;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.springframework.stereotype.Service;
50 import javax.inject.Inject;
51 import javax.inject.Named;
53 import java.io.IOException;
54 import java.util.ArrayList;
55 import java.util.Arrays;
56 import java.util.HashMap;
57 import java.util.List;
61 * FIXME remove all generic Exception to have usefull ones
62 * FIXME review the staging mechanism to have a per user session one
64 * @author Olivier Lamy
66 @Service( "managedRepositoryAdmin#default" )
67 public class DefaultManagedRepositoryAdmin
68 extends AbstractRepositoryAdmin
69 implements ManagedRepositoryAdmin
72 public static final String REPOSITORY_LOCATION_VALID_EXPRESSION = "^[-a-zA-Z0-9._/~:?!&=\\\\]+$";
74 private Logger log = LoggerFactory.getLogger( getClass() );
76 public static final String STAGE_REPO_ID_END = "-stage";
80 @Named( value = "archivaTaskScheduler#repository" )
81 private RepositoryArchivaTaskScheduler repositoryTaskScheduler;
84 private RepositorySessionFactory repositorySessionFactory;
87 private RepositoryStatisticsManager repositoryStatisticsManager;
91 protected RoleManager roleManager;
93 public List<ManagedRepository> getManagedRepositories()
94 throws RepositoryAdminException
96 List<ManagedRepositoryConfiguration> managedRepoConfigs =
97 getArchivaConfiguration().getConfiguration().getManagedRepositories();
99 List<ManagedRepository> managedRepos = new ArrayList<ManagedRepository>( managedRepoConfigs.size() );
101 for ( ManagedRepositoryConfiguration repoConfig : managedRepoConfigs )
103 // TODO add staging repo information back too
104 ManagedRepository repo =
105 new ManagedRepository( repoConfig.getId(), repoConfig.getName(), repoConfig.getLocation(),
106 repoConfig.getLayout(), repoConfig.isSnapshots(), repoConfig.isReleases(),
107 repoConfig.isBlockRedeployments(), repoConfig.getRefreshCronExpression(),
108 repoConfig.getIndexDir(), repoConfig.isScanned(), repoConfig.getDaysOlder(),
109 repoConfig.getRetentionCount(), repoConfig.isDeleteReleasedSnapshots() );
111 managedRepos.add( repo );
117 public Map<String, ManagedRepository> getManagedRepositoriesAsMap()
118 throws RepositoryAdminException
120 List<ManagedRepository> managedRepositories = getManagedRepositories();
121 Map<String, ManagedRepository> repositoriesMap =
122 new HashMap<String, ManagedRepository>( managedRepositories.size() );
123 for ( ManagedRepository managedRepository : managedRepositories )
125 repositoriesMap.put( managedRepository.getId(), managedRepository );
127 return repositoriesMap;
130 public ManagedRepository getManagedRepository( String repositoryId )
131 throws RepositoryAdminException
133 List<ManagedRepository> repos = getManagedRepositories();
134 for ( ManagedRepository repo : repos )
136 if ( StringUtils.equals( repo.getId(), repositoryId ) )
144 public Boolean addManagedRepository( ManagedRepository managedRepository, boolean needStageRepo,
145 AuditInformation auditInformation )
146 throws RepositoryAdminException
149 getRepositoryCommonValidator().basicValidation( managedRepository, false );
150 triggerAuditEvent( managedRepository.getId(), null, AuditEvent.ADD_MANAGED_REPO, auditInformation );
152 addManagedRepository( managedRepository.getId(), managedRepository.getLayout(), managedRepository.getName(),
153 managedRepository.getLocation(), managedRepository.isBlockRedeployments(),
154 managedRepository.isReleases(), managedRepository.isSnapshots(), needStageRepo,
155 managedRepository.getCronExpression(), managedRepository.getIndexDirectory(),
156 managedRepository.getDaysOlder(), managedRepository.getRetentionCount(),
157 managedRepository.isDeleteReleasedSnapshots(), auditInformation,
158 getArchivaConfiguration().getConfiguration() ) != null;
162 private ManagedRepositoryConfiguration addManagedRepository( String repoId, String layout, String name,
163 String location, boolean blockRedeployments,
164 boolean releasesIncluded, boolean snapshotsIncluded,
165 boolean stageRepoNeeded, String cronExpression,
166 String indexDir, int daysOlder, int retentionCount,
167 boolean deteleReleasedSnapshots,
168 AuditInformation auditInformation,
169 Configuration config )
170 throws RepositoryAdminException
173 // FIXME : olamy can be empty to avoid scheduled scan ?
174 if ( StringUtils.isNotBlank( cronExpression ) )
176 CronExpressionValidator validator = new CronExpressionValidator();
178 if ( !validator.validate( cronExpression ) )
180 throw new RepositoryAdminException( "Invalid cron expression." );
185 throw new RepositoryAdminException( "Cron expression cannot be empty." );
188 String repoLocation = getRepositoryCommonValidator().removeExpressions( location );
190 if ( !GenericValidator.matchRegexp( repoLocation, REPOSITORY_LOCATION_VALID_EXPRESSION ) )
192 throw new RepositoryAdminException(
193 "Invalid repository location. Directory must only contain alphanumeric characters, equals(=), question-marks(?), "
194 + "exclamation-points(!), ampersands(&), forward-slashes(/), back-slashes(\\), underscores(_), dots(.), colons(:), tildes(~), and dashes(-)." );
197 ManagedRepositoryConfiguration repository = new ManagedRepositoryConfiguration();
199 repository.setId( repoId );
200 repository.setBlockRedeployments( blockRedeployments );
201 repository.setReleases( releasesIncluded );
202 repository.setSnapshots( snapshotsIncluded );
203 repository.setName( name );
204 repository.setLocation( repoLocation );
205 repository.setLayout( layout );
206 repository.setRefreshCronExpression( cronExpression );
207 repository.setIndexDir( indexDir );
208 repository.setDaysOlder( daysOlder );
209 repository.setRetentionCount( retentionCount );
210 repository.setDeleteReleasedSnapshots( deteleReleasedSnapshots );
215 addRepository( repository, config );
216 addRepositoryRoles( repository );
218 if ( stageRepoNeeded )
220 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( repository );
221 addRepository( stagingRepository, config );
222 addRepositoryRoles( stagingRepository );
223 triggerAuditEvent( stagingRepository.getId(), null, AuditEvent.ADD_MANAGED_REPO, auditInformation );
226 catch ( RoleManagerException e )
228 throw new RepositoryAdminException( "failed to add repository roles " + e.getMessage(), e );
230 catch ( IOException e )
232 throw new RepositoryAdminException( "failed to add repository " + e.getMessage(), e );
235 saveConfiguration( config );
237 //MRM-1342 Repository statistics report doesn't appear to be working correctly
238 //scan repository when adding of repository is successful
241 scanRepository( repoId, true );
242 // olamy no need of scanning staged repo
244 if ( stageRepoNeeded )
246 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( repository );
247 scanRepository( stagingRepository.getId(), true );
250 catch ( Exception e )
252 log.warn( new StringBuilder( "Unable to scan repository [" ).append( repoId ).append( "]: " ).append(
253 e.getMessage() ).toString(), e );
260 // FIXME cleanup repositoryGroups when deleting a ManagedRepo
261 public Boolean deleteManagedRepository( String repositoryId, AuditInformation auditInformation,
262 boolean deleteContent )
263 throws RepositoryAdminException
265 Configuration config = getArchivaConfiguration().getConfiguration();
267 ManagedRepositoryConfiguration repository = config.findManagedRepositoryById( repositoryId );
269 if ( repository == null )
271 throw new RepositoryAdminException( "A repository with that id does not exist" );
274 triggerAuditEvent( repositoryId, null, AuditEvent.DELETE_MANAGED_REPO, auditInformation );
276 deleteManagedRepository( repository, deleteContent, config, false );
278 // stage repo exists ?
279 ManagedRepositoryConfiguration stagingRepository =
280 getArchivaConfiguration().getConfiguration().findManagedRepositoryById( repositoryId + STAGE_REPO_ID_END );
281 if ( stagingRepository != null )
283 // do not trigger event when deleting the staged one
284 //triggerAuditEvent( stagingRepository.getId(), null, AuditEvent.DELETE_MANAGED_REPO, auditInformation );
285 deleteManagedRepository( stagingRepository, deleteContent, config, true );
290 saveConfiguration( config );
292 catch ( Exception e )
294 throw new RepositoryAdminException( "Error saving configuration for delete action" + e.getMessage() );
300 private Boolean deleteManagedRepository( ManagedRepositoryConfiguration repository, boolean deleteContent,
301 Configuration config, boolean stagedOne )
302 throws RepositoryAdminException
306 RepositorySession repositorySession = getRepositorySessionFactory().createSession();
309 MetadataRepository metadataRepository = repositorySession.getRepository();
310 metadataRepository.removeRepository( repository.getId() );
311 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
312 getRepositoryStatisticsManager().deleteStatistics( metadataRepository, repository.getId() );
313 repositorySession.save();
315 catch ( MetadataRepositoryException e )
317 throw new RepositoryAdminException( e.getMessage(), e );
321 repositorySession.close();
324 config.removeManagedRepository( repository );
328 // TODO could be async ? as directory can be huge
329 File dir = new File( repository.getLocation() );
330 if ( !FileUtils.deleteQuietly( dir ) )
332 throw new RepositoryAdminException( "Cannot delete repository " + dir );
336 // olamy: copy list for reading as a unit test in webapp fail with ConcurrentModificationException
337 List<ProxyConnectorConfiguration> proxyConnectors =
338 new ArrayList<ProxyConnectorConfiguration>( config.getProxyConnectors() );
339 for ( ProxyConnectorConfiguration proxyConnector : proxyConnectors )
341 if ( StringUtils.equals( proxyConnector.getSourceRepoId(), repository.getId() ) )
343 config.removeProxyConnector( proxyConnector );
347 Map<String, List<String>> repoToGroupMap = config.getRepositoryToGroupMap();
348 if ( repoToGroupMap != null )
350 if ( repoToGroupMap.containsKey( repository.getId() ) )
352 List<String> repoGroups = repoToGroupMap.get( repository.getId() );
353 for ( String repoGroup : repoGroups )
355 // copy to prevent UnsupportedOperationException
356 RepositoryGroupConfiguration repositoryGroupConfiguration =
357 config.findRepositoryGroupById( repoGroup );
358 List<String> repos = new ArrayList<String>( repositoryGroupConfiguration.getRepositories() );
359 config.removeRepositoryGroup( repositoryGroupConfiguration );
360 repos.remove( repository.getId() );
361 repositoryGroupConfiguration.setRepositories( repos );
362 config.addRepositoryGroup( repositoryGroupConfiguration );
369 removeRepositoryRoles( repository );
371 catch ( RoleManagerException e )
373 throw new RepositoryAdminException(
374 "fail to remove repository roles for repository " + repository.getId() + " : " + e.getMessage(), e );
377 saveConfiguration( config );
383 public Boolean updateManagedRepository( ManagedRepository managedRepository, boolean needStageRepo,
384 AuditInformation auditInformation, boolean resetStats )
385 throws RepositoryAdminException
388 log.debug( "updateManagedConfiguration repo {} needStage {} resetStats {} ",
389 Arrays.asList( managedRepository, needStageRepo, resetStats ).toArray() );
391 // Ensure that the fields are valid.
393 getRepositoryCommonValidator().basicValidation( managedRepository, true );
395 Configuration configuration = getArchivaConfiguration().getConfiguration();
397 ManagedRepositoryConfiguration toremove = configuration.findManagedRepositoryById( managedRepository.getId() );
399 if ( toremove != null )
401 configuration.removeManagedRepository( toremove );
404 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( toremove );
406 // TODO remove content from old if path has changed !!!!!
408 if ( stagingRepository != null )
410 configuration.removeManagedRepository( stagingRepository );
413 ManagedRepositoryConfiguration managedRepositoryConfiguration =
414 addManagedRepository( managedRepository.getId(), managedRepository.getLayout(), managedRepository.getName(),
415 managedRepository.getLocation(), managedRepository.isBlockRedeployments(),
416 managedRepository.isReleases(), managedRepository.isSnapshots(), needStageRepo,
417 managedRepository.getCronExpression(), managedRepository.getIndexDirectory(),
418 managedRepository.getDaysOlder(), managedRepository.getRetentionCount(),
419 managedRepository.isDeleteReleasedSnapshots(), auditInformation,
420 getArchivaConfiguration().getConfiguration() );
422 // Save the repository configuration.
423 RepositorySession repositorySession = getRepositorySessionFactory().createSession();
427 triggerAuditEvent( managedRepositoryConfiguration.getId(), null, AuditEvent.MODIFY_MANAGED_REPO,
430 saveConfiguration( this.getArchivaConfiguration().getConfiguration() );
433 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
434 getRepositoryStatisticsManager().deleteStatistics( repositorySession.getRepository(),
435 managedRepositoryConfiguration.getId() );
436 repositorySession.save();
440 catch ( MetadataRepositoryException e )
442 throw new RepositoryAdminException( e.getMessage(), e );
446 repositorySession.close();
452 //--------------------------
454 //--------------------------
457 protected void addRepository( ManagedRepositoryConfiguration repository, Configuration configuration )
458 throws RepositoryAdminException, IOException
460 // Normalize the path
461 File file = new File( repository.getLocation() );
462 repository.setLocation( file.getCanonicalPath() );
463 if ( !file.exists() )
467 if ( !file.exists() || !file.isDirectory() )
469 throw new RepositoryAdminException(
470 "Unable to add repository - no write access, can not create the root directory: " + file );
473 configuration.addManagedRepository( repository );
476 private ManagedRepositoryConfiguration getStageRepoConfig( ManagedRepositoryConfiguration repository )
478 ManagedRepositoryConfiguration stagingRepository = new ManagedRepositoryConfiguration();
479 stagingRepository.setId( repository.getId() + STAGE_REPO_ID_END );
480 stagingRepository.setLayout( repository.getLayout() );
481 stagingRepository.setName( repository.getName() + STAGE_REPO_ID_END );
482 stagingRepository.setBlockRedeployments( repository.isBlockRedeployments() );
483 stagingRepository.setDaysOlder( repository.getDaysOlder() );
484 stagingRepository.setDeleteReleasedSnapshots( repository.isDeleteReleasedSnapshots() );
485 stagingRepository.setIndexDir( repository.getIndexDir() );
486 String path = repository.getLocation();
487 int lastIndex = path.lastIndexOf( '/' );
488 stagingRepository.setLocation( path.substring( 0, lastIndex ) + "/" + stagingRepository.getId() );
489 stagingRepository.setRefreshCronExpression( repository.getRefreshCronExpression() );
490 stagingRepository.setReleases( repository.isReleases() );
491 stagingRepository.setRetentionCount( repository.getRetentionCount() );
492 stagingRepository.setScanned( repository.isScanned() );
493 stagingRepository.setSnapshots( repository.isSnapshots() );
494 return stagingRepository;
497 public Boolean scanRepository( String repositoryId, boolean fullScan )
499 if ( getRepositoryTaskScheduler().isProcessingRepositoryTask( repositoryId ) )
501 log.info( "scanning of repository with id {} already scheduled", repositoryId );
503 RepositoryTask task = new RepositoryTask();
504 task.setRepositoryId( repositoryId );
505 task.setScanAll( fullScan );
508 getRepositoryTaskScheduler().queueTask( task );
510 catch ( TaskQueueException e )
512 log.error( "failed to schedule scanning of repo with id {}", repositoryId, e );
518 protected void addRepositoryRoles( ManagedRepositoryConfiguration newRepository )
519 throws RoleManagerException
521 String repoId = newRepository.getId();
523 // TODO: double check these are configured on start up
524 // TODO: belongs in the business logic
526 if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
528 getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
531 if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
533 getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
537 protected void removeRepositoryRoles( ManagedRepositoryConfiguration existingRepository )
538 throws RoleManagerException
540 String repoId = existingRepository.getId();
542 if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
544 getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
547 if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
549 getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
552 log.debug( "removed user roles associated with repository {}", repoId );
555 //--------------------------
557 //--------------------------
560 public RoleManager getRoleManager()
565 public void setRoleManager( RoleManager roleManager )
567 this.roleManager = roleManager;
570 public RepositoryStatisticsManager getRepositoryStatisticsManager()
572 return repositoryStatisticsManager;
575 public void setRepositoryStatisticsManager( RepositoryStatisticsManager repositoryStatisticsManager )
577 this.repositoryStatisticsManager = repositoryStatisticsManager;
580 public RepositorySessionFactory getRepositorySessionFactory()
582 return repositorySessionFactory;
585 public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory )
587 this.repositorySessionFactory = repositorySessionFactory;
591 public RepositoryArchivaTaskScheduler getRepositoryTaskScheduler()
593 return repositoryTaskScheduler;
596 public void setRepositoryTaskScheduler( RepositoryArchivaTaskScheduler repositoryTaskScheduler )
598 this.repositoryTaskScheduler = repositoryTaskScheduler;