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 );
266 public Boolean deleteManagedRepository( String repositoryId, AuditInformation auditInformation,
267 boolean deleteContent )
268 throws RepositoryAdminException
270 Configuration config = getArchivaConfiguration().getConfiguration();
272 ManagedRepositoryConfiguration repository = config.findManagedRepositoryById( repositoryId );
274 if ( repository == null )
276 throw new RepositoryAdminException( "A repository with that id does not exist" );
279 triggerAuditEvent( repositoryId, null, AuditEvent.DELETE_MANAGED_REPO, auditInformation );
281 deleteManagedRepository( repository, deleteContent, config, false );
283 // stage repo exists ?
284 ManagedRepositoryConfiguration stagingRepository =
285 getArchivaConfiguration().getConfiguration().findManagedRepositoryById( repositoryId + STAGE_REPO_ID_END );
286 if ( stagingRepository != null )
288 // do not trigger event when deleting the staged one
289 deleteManagedRepository( stagingRepository, deleteContent, config, true );
294 saveConfiguration( config );
296 catch ( Exception e )
298 throw new RepositoryAdminException( "Error saving configuration for delete action" + e.getMessage() );
304 private Boolean deleteManagedRepository( ManagedRepositoryConfiguration repository, boolean deleteContent,
305 Configuration config, boolean stagedOne )
306 throws RepositoryAdminException
311 NexusIndexer nexusIndexer = plexusSisuBridge.lookup( NexusIndexer.class );
313 IndexingContext context = nexusIndexer.getIndexingContexts().get( repository.getId() );
314 if ( context != null )
316 nexusIndexer.removeIndexingContext( context, deleteContent );
319 catch ( PlexusSisuBridgeException e )
321 throw new RepositoryAdminException( e.getMessage(), e );
323 catch ( IOException e )
325 throw new RepositoryAdminException( e.getMessage(), e );
329 RepositorySession repositorySession = getRepositorySessionFactory().createSession();
332 MetadataRepository metadataRepository = repositorySession.getRepository();
333 metadataRepository.removeRepository( repository.getId() );
334 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
335 getRepositoryStatisticsManager().deleteStatistics( metadataRepository, repository.getId() );
336 repositorySession.save();
338 catch ( MetadataRepositoryException e )
340 throw new RepositoryAdminException( e.getMessage(), e );
344 repositorySession.close();
347 config.removeManagedRepository( repository );
351 // TODO could be async ? as directory can be huge
352 File dir = new File( repository.getLocation() );
353 if ( !FileUtils.deleteQuietly( dir ) )
355 throw new RepositoryAdminException( "Cannot delete repository " + dir );
359 // olamy: copy list for reading as a unit test in webapp fail with ConcurrentModificationException
360 List<ProxyConnectorConfiguration> proxyConnectors =
361 new ArrayList<ProxyConnectorConfiguration>( config.getProxyConnectors() );
362 for ( ProxyConnectorConfiguration proxyConnector : proxyConnectors )
364 if ( StringUtils.equals( proxyConnector.getSourceRepoId(), repository.getId() ) )
366 config.removeProxyConnector( proxyConnector );
370 Map<String, List<String>> repoToGroupMap = config.getRepositoryToGroupMap();
371 if ( repoToGroupMap != null )
373 if ( repoToGroupMap.containsKey( repository.getId() ) )
375 List<String> repoGroups = repoToGroupMap.get( repository.getId() );
376 for ( String repoGroup : repoGroups )
378 // copy to prevent UnsupportedOperationException
379 RepositoryGroupConfiguration repositoryGroupConfiguration =
380 config.findRepositoryGroupById( repoGroup );
381 List<String> repos = new ArrayList<String>( repositoryGroupConfiguration.getRepositories() );
382 config.removeRepositoryGroup( repositoryGroupConfiguration );
383 repos.remove( repository.getId() );
384 repositoryGroupConfiguration.setRepositories( repos );
385 config.addRepositoryGroup( repositoryGroupConfiguration );
392 removeRepositoryRoles( repository );
394 catch ( RoleManagerException e )
396 throw new RepositoryAdminException(
397 "fail to remove repository roles for repository " + repository.getId() + " : " + e.getMessage(), e );
400 saveConfiguration( config );
406 public Boolean updateManagedRepository( ManagedRepository managedRepository, boolean needStageRepo,
407 AuditInformation auditInformation, boolean resetStats )
408 throws RepositoryAdminException
411 log.debug( "updateManagedConfiguration repo {} needStage {} resetStats {} ",
412 Arrays.asList( managedRepository, needStageRepo, resetStats ).toArray() );
414 // Ensure that the fields are valid.
416 getRepositoryCommonValidator().basicValidation( managedRepository, true );
418 Configuration configuration = getArchivaConfiguration().getConfiguration();
420 ManagedRepositoryConfiguration toremove = configuration.findManagedRepositoryById( managedRepository.getId() );
422 if ( toremove != null )
424 configuration.removeManagedRepository( toremove );
427 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( toremove );
429 // TODO remove content from old if path has changed !!!!!
431 if ( stagingRepository != null )
433 configuration.removeManagedRepository( stagingRepository );
436 ManagedRepositoryConfiguration managedRepositoryConfiguration =
437 addManagedRepository( managedRepository.getId(), managedRepository.getLayout(), managedRepository.getName(),
438 managedRepository.getLocation(), managedRepository.isBlockRedeployments(),
439 managedRepository.isReleases(), managedRepository.isSnapshots(), needStageRepo,
440 managedRepository.getCronExpression(), managedRepository.getIndexDirectory(),
441 managedRepository.getDaysOlder(), managedRepository.getRetentionCount(),
442 managedRepository.isDeleteReleasedSnapshots(), auditInformation,
443 getArchivaConfiguration().getConfiguration() );
445 // Save the repository configuration.
446 RepositorySession repositorySession = getRepositorySessionFactory().createSession();
450 triggerAuditEvent( managedRepositoryConfiguration.getId(), null, AuditEvent.MODIFY_MANAGED_REPO,
453 saveConfiguration( this.getArchivaConfiguration().getConfiguration() );
456 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
457 getRepositoryStatisticsManager().deleteStatistics( repositorySession.getRepository(),
458 managedRepositoryConfiguration.getId() );
459 repositorySession.save();
463 catch ( MetadataRepositoryException e )
465 throw new RepositoryAdminException( e.getMessage(), e );
469 repositorySession.close();
475 //--------------------------
477 //--------------------------
480 protected void addRepository( ManagedRepositoryConfiguration repository, Configuration configuration )
481 throws RepositoryAdminException, IOException
483 // Normalize the path
484 File file = new File( repository.getLocation() );
485 repository.setLocation( file.getCanonicalPath() );
486 if ( !file.exists() )
490 if ( !file.exists() || !file.isDirectory() )
492 throw new RepositoryAdminException(
493 "Unable to add repository - no write access, can not create the root directory: " + file );
496 configuration.addManagedRepository( repository );
499 private ManagedRepositoryConfiguration getStageRepoConfig( ManagedRepositoryConfiguration repository )
501 ManagedRepositoryConfiguration stagingRepository = new ManagedRepositoryConfiguration();
502 stagingRepository.setId( repository.getId() + STAGE_REPO_ID_END );
503 stagingRepository.setLayout( repository.getLayout() );
504 stagingRepository.setName( repository.getName() + STAGE_REPO_ID_END );
505 stagingRepository.setBlockRedeployments( repository.isBlockRedeployments() );
506 stagingRepository.setDaysOlder( repository.getDaysOlder() );
507 stagingRepository.setDeleteReleasedSnapshots( repository.isDeleteReleasedSnapshots() );
508 stagingRepository.setIndexDir( repository.getIndexDir() );
509 String path = repository.getLocation();
510 int lastIndex = path.lastIndexOf( '/' );
511 stagingRepository.setLocation( path.substring( 0, lastIndex ) + "/" + stagingRepository.getId() );
512 stagingRepository.setRefreshCronExpression( repository.getRefreshCronExpression() );
513 stagingRepository.setReleases( repository.isReleases() );
514 stagingRepository.setRetentionCount( repository.getRetentionCount() );
515 stagingRepository.setScanned( repository.isScanned() );
516 stagingRepository.setSnapshots( repository.isSnapshots() );
517 return stagingRepository;
520 public Boolean scanRepository( String repositoryId, boolean fullScan )
522 if ( getRepositoryTaskScheduler().isProcessingRepositoryTask( repositoryId ) )
524 log.info( "scanning of repository with id {} already scheduled", repositoryId );
526 RepositoryTask task = new RepositoryTask();
527 task.setRepositoryId( repositoryId );
528 task.setScanAll( fullScan );
531 getRepositoryTaskScheduler().queueTask( task );
533 catch ( TaskQueueException e )
535 log.error( "failed to schedule scanning of repo with id {}", repositoryId, e );
541 protected void addRepositoryRoles( ManagedRepositoryConfiguration newRepository )
542 throws RoleManagerException
544 String repoId = newRepository.getId();
546 // TODO: double check these are configured on start up
547 // TODO: belongs in the business logic
549 if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
551 getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
554 if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
556 getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
560 protected void removeRepositoryRoles( ManagedRepositoryConfiguration existingRepository )
561 throws RoleManagerException
563 String repoId = existingRepository.getId();
565 if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
567 getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
570 if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
572 getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
575 log.debug( "removed user roles associated with repository {}", repoId );
578 //--------------------------
580 //--------------------------
583 public RoleManager getRoleManager()
588 public void setRoleManager( RoleManager roleManager )
590 this.roleManager = roleManager;
593 public RepositoryStatisticsManager getRepositoryStatisticsManager()
595 return repositoryStatisticsManager;
598 public void setRepositoryStatisticsManager( RepositoryStatisticsManager repositoryStatisticsManager )
600 this.repositoryStatisticsManager = repositoryStatisticsManager;
603 public RepositorySessionFactory getRepositorySessionFactory()
605 return repositorySessionFactory;
608 public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory )
610 this.repositorySessionFactory = repositorySessionFactory;
614 public RepositoryArchivaTaskScheduler getRepositoryTaskScheduler()
616 return repositoryTaskScheduler;
619 public void setRepositoryTaskScheduler( RepositoryArchivaTaskScheduler repositoryTaskScheduler )
621 this.repositoryTaskScheduler = repositoryTaskScheduler;