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.MavenIndexerUtils;
28 import org.apache.archiva.common.plexusbridge.PlexusSisuBridge;
29 import org.apache.archiva.common.plexusbridge.PlexusSisuBridgeException;
30 import org.apache.archiva.configuration.Configuration;
31 import org.apache.archiva.configuration.ManagedRepositoryConfiguration;
32 import org.apache.archiva.configuration.ProxyConnectorConfiguration;
33 import org.apache.archiva.configuration.RepositoryGroupConfiguration;
34 import org.apache.archiva.metadata.repository.MetadataRepository;
35 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
36 import org.apache.archiva.metadata.repository.RepositorySession;
37 import org.apache.archiva.metadata.repository.RepositorySessionFactory;
38 import org.apache.archiva.metadata.repository.stats.RepositoryStatisticsManager;
39 import org.apache.archiva.scheduler.repository.RepositoryArchivaTaskScheduler;
40 import org.apache.archiva.scheduler.repository.RepositoryTask;
41 import org.apache.archiva.security.common.ArchivaRoleConstants;
42 import org.apache.commons.io.FileUtils;
43 import org.apache.commons.lang.StringUtils;
44 import org.apache.commons.validator.GenericValidator;
45 import org.apache.maven.index.NexusIndexer;
46 import org.apache.maven.index.context.IndexCreator;
47 import org.apache.maven.index.context.IndexingContext;
48 import org.apache.maven.index.context.UnsupportedExistingLuceneIndexException;
49 import org.codehaus.plexus.redback.role.RoleManager;
50 import org.codehaus.plexus.redback.role.RoleManagerException;
51 import org.codehaus.plexus.taskqueue.TaskQueueException;
52 import org.codehaus.redback.components.scheduler.CronExpressionValidator;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55 import org.springframework.stereotype.Service;
57 import javax.annotation.PostConstruct;
58 import javax.inject.Inject;
59 import javax.inject.Named;
61 import java.io.IOException;
62 import java.net.MalformedURLException;
63 import java.util.ArrayList;
64 import java.util.Arrays;
65 import java.util.HashMap;
66 import java.util.List;
70 * FIXME remove all generic Exception to have usefull ones
71 * FIXME review the staging mechanism to have a per user session one
73 * @author Olivier Lamy
75 @Service( "managedRepositoryAdmin#default" )
76 public class DefaultManagedRepositoryAdmin
77 extends AbstractRepositoryAdmin
78 implements ManagedRepositoryAdmin
81 public static final String REPOSITORY_LOCATION_VALID_EXPRESSION = "^[-a-zA-Z0-9._/~:?!&=\\\\]+$";
83 private Logger log = LoggerFactory.getLogger( getClass() );
85 public static final String STAGE_REPO_ID_END = "-stage";
89 @Named( value = "archivaTaskScheduler#repository" )
90 private RepositoryArchivaTaskScheduler repositoryTaskScheduler;
93 private RepositorySessionFactory repositorySessionFactory;
96 private RepositoryStatisticsManager repositoryStatisticsManager;
99 private PlexusSisuBridge plexusSisuBridge;
102 private MavenIndexerUtils mavenIndexerUtils;
105 protected RoleManager roleManager;
108 private void initialize()
109 throws RepositoryAdminException
111 // initialize index context on start
112 for ( ManagedRepository managedRepository : getManagedRepositories() )
114 createIndexContext( managedRepository );
118 public List<ManagedRepository> getManagedRepositories()
119 throws RepositoryAdminException
121 List<ManagedRepositoryConfiguration> managedRepoConfigs =
122 getArchivaConfiguration().getConfiguration().getManagedRepositories();
124 List<ManagedRepository> managedRepos = new ArrayList<ManagedRepository>( managedRepoConfigs.size() );
126 for ( ManagedRepositoryConfiguration repoConfig : managedRepoConfigs )
128 // TODO add staging repo information back too
129 ManagedRepository repo =
130 new ManagedRepository( repoConfig.getId(), repoConfig.getName(), repoConfig.getLocation(),
131 repoConfig.getLayout(), repoConfig.isSnapshots(), repoConfig.isReleases(),
132 repoConfig.isBlockRedeployments(), repoConfig.getRefreshCronExpression(),
133 repoConfig.getIndexDir(), repoConfig.isScanned(), repoConfig.getDaysOlder(),
134 repoConfig.getRetentionCount(), repoConfig.isDeleteReleasedSnapshots(), false );
136 managedRepos.add( repo );
142 public Map<String, ManagedRepository> getManagedRepositoriesAsMap()
143 throws RepositoryAdminException
145 List<ManagedRepository> managedRepositories = getManagedRepositories();
146 Map<String, ManagedRepository> repositoriesMap =
147 new HashMap<String, ManagedRepository>( managedRepositories.size() );
148 for ( ManagedRepository managedRepository : managedRepositories )
150 repositoriesMap.put( managedRepository.getId(), managedRepository );
152 return repositoriesMap;
155 public ManagedRepository getManagedRepository( String repositoryId )
156 throws RepositoryAdminException
158 List<ManagedRepository> repos = getManagedRepositories();
159 for ( ManagedRepository repo : repos )
161 if ( StringUtils.equals( repo.getId(), repositoryId ) )
169 public Boolean addManagedRepository( ManagedRepository managedRepository, boolean needStageRepo,
170 AuditInformation auditInformation )
171 throws RepositoryAdminException
174 getRepositoryCommonValidator().basicValidation( managedRepository, false );
175 triggerAuditEvent( managedRepository.getId(), null, AuditEvent.ADD_MANAGED_REPO, auditInformation );
177 addManagedRepository( managedRepository.getId(), managedRepository.getLayout(), managedRepository.getName(),
178 managedRepository.getLocation(), managedRepository.isBlockRedeployments(),
179 managedRepository.isReleases(), managedRepository.isSnapshots(), needStageRepo,
180 managedRepository.getCronExpression(), managedRepository.getIndexDirectory(),
181 managedRepository.getDaysOlder(), managedRepository.getRetentionCount(),
182 managedRepository.isDeleteReleasedSnapshots(), auditInformation,
183 getArchivaConfiguration().getConfiguration() ) != null;
185 createIndexContext( managedRepository );
190 private ManagedRepositoryConfiguration addManagedRepository( String repoId, String layout, String name,
191 String location, boolean blockRedeployments,
192 boolean releasesIncluded, boolean snapshotsIncluded,
193 boolean stageRepoNeeded, String cronExpression,
194 String indexDir, int daysOlder, int retentionCount,
195 boolean deteleReleasedSnapshots,
196 AuditInformation auditInformation,
197 Configuration config )
198 throws RepositoryAdminException
201 // FIXME : olamy can be empty to avoid scheduled scan ?
202 if ( StringUtils.isNotBlank( cronExpression ) )
204 CronExpressionValidator validator = new CronExpressionValidator();
206 if ( !validator.validate( cronExpression ) )
208 throw new RepositoryAdminException( "Invalid cron expression." );
213 throw new RepositoryAdminException( "Cron expression cannot be empty." );
216 String repoLocation = getRepositoryCommonValidator().removeExpressions( location );
218 if ( !GenericValidator.matchRegexp( repoLocation, REPOSITORY_LOCATION_VALID_EXPRESSION ) )
220 throw new RepositoryAdminException(
221 "Invalid repository location. Directory must only contain alphanumeric characters, equals(=), question-marks(?), "
222 + "exclamation-points(!), ampersands(&), forward-slashes(/), back-slashes(\\), underscores(_), dots(.), colons(:), tildes(~), and dashes(-)." );
225 ManagedRepositoryConfiguration repository = new ManagedRepositoryConfiguration();
227 repository.setId( repoId );
228 repository.setBlockRedeployments( blockRedeployments );
229 repository.setReleases( releasesIncluded );
230 repository.setSnapshots( snapshotsIncluded );
231 repository.setName( name );
232 repository.setLocation( repoLocation );
233 repository.setLayout( layout );
234 repository.setRefreshCronExpression( cronExpression );
235 repository.setIndexDir( indexDir );
236 repository.setDaysOlder( daysOlder );
237 repository.setRetentionCount( retentionCount );
238 repository.setDeleteReleasedSnapshots( deteleReleasedSnapshots );
239 repository.setIndexDir( indexDir );
243 addRepository( repository, config );
244 addRepositoryRoles( repository );
246 if ( stageRepoNeeded )
248 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( repository );
249 addRepository( stagingRepository, config );
250 addRepositoryRoles( stagingRepository );
251 triggerAuditEvent( stagingRepository.getId(), null, AuditEvent.ADD_MANAGED_REPO, auditInformation );
254 catch ( RoleManagerException e )
256 throw new RepositoryAdminException( "failed to add repository roles " + e.getMessage(), e );
258 catch ( IOException e )
260 throw new RepositoryAdminException( "failed to add repository " + e.getMessage(), e );
263 saveConfiguration( config );
265 //MRM-1342 Repository statistics report doesn't appear to be working correctly
266 //scan repository when adding of repository is successful
269 scanRepository( repoId, true );
270 // olamy no need of scanning staged repo
272 if ( stageRepoNeeded )
274 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( repository );
275 scanRepository( stagingRepository.getId(), true );
278 catch ( Exception e )
280 log.warn( new StringBuilder( "Unable to scan repository [" ).append( repoId ).append( "]: " ).append(
281 e.getMessage() ).toString(), e );
287 public Boolean deleteManagedRepository( String repositoryId, AuditInformation auditInformation,
288 boolean deleteContent )
289 throws RepositoryAdminException
291 Configuration config = getArchivaConfiguration().getConfiguration();
293 ManagedRepositoryConfiguration repository = config.findManagedRepositoryById( repositoryId );
295 if ( repository == null )
297 throw new RepositoryAdminException( "A repository with that id does not exist" );
300 triggerAuditEvent( repositoryId, null, AuditEvent.DELETE_MANAGED_REPO, auditInformation );
302 deleteManagedRepository( repository, deleteContent, config, false );
304 // stage repo exists ?
305 ManagedRepositoryConfiguration stagingRepository =
306 getArchivaConfiguration().getConfiguration().findManagedRepositoryById( repositoryId + STAGE_REPO_ID_END );
307 if ( stagingRepository != null )
309 // do not trigger event when deleting the staged one
310 deleteManagedRepository( stagingRepository, deleteContent, config, true );
315 saveConfiguration( config );
317 catch ( Exception e )
319 throw new RepositoryAdminException( "Error saving configuration for delete action" + e.getMessage() );
325 private Boolean deleteManagedRepository( ManagedRepositoryConfiguration repository, boolean deleteContent,
326 Configuration config, boolean stagedOne )
327 throws RepositoryAdminException
332 NexusIndexer nexusIndexer = plexusSisuBridge.lookup( NexusIndexer.class );
334 IndexingContext context = nexusIndexer.getIndexingContexts().get( repository.getId() );
335 if ( context != null )
337 nexusIndexer.removeIndexingContext( context, deleteContent );
340 catch ( PlexusSisuBridgeException e )
342 throw new RepositoryAdminException( e.getMessage(), e );
344 catch ( IOException e )
346 throw new RepositoryAdminException( e.getMessage(), e );
350 RepositorySession repositorySession = getRepositorySessionFactory().createSession();
353 MetadataRepository metadataRepository = repositorySession.getRepository();
354 metadataRepository.removeRepository( repository.getId() );
355 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
356 getRepositoryStatisticsManager().deleteStatistics( metadataRepository, repository.getId() );
357 repositorySession.save();
359 catch ( MetadataRepositoryException e )
361 throw new RepositoryAdminException( e.getMessage(), e );
365 repositorySession.close();
368 config.removeManagedRepository( repository );
372 // TODO could be async ? as directory can be huge
373 File dir = new File( repository.getLocation() );
374 if ( !FileUtils.deleteQuietly( dir ) )
376 throw new RepositoryAdminException( "Cannot delete repository " + dir );
380 // olamy: copy list for reading as a unit test in webapp fail with ConcurrentModificationException
381 List<ProxyConnectorConfiguration> proxyConnectors =
382 new ArrayList<ProxyConnectorConfiguration>( config.getProxyConnectors() );
383 for ( ProxyConnectorConfiguration proxyConnector : proxyConnectors )
385 if ( StringUtils.equals( proxyConnector.getSourceRepoId(), repository.getId() ) )
387 config.removeProxyConnector( proxyConnector );
391 Map<String, List<String>> repoToGroupMap = config.getRepositoryToGroupMap();
392 if ( repoToGroupMap != null )
394 if ( repoToGroupMap.containsKey( repository.getId() ) )
396 List<String> repoGroups = repoToGroupMap.get( repository.getId() );
397 for ( String repoGroup : repoGroups )
399 // copy to prevent UnsupportedOperationException
400 RepositoryGroupConfiguration repositoryGroupConfiguration =
401 config.findRepositoryGroupById( repoGroup );
402 List<String> repos = new ArrayList<String>( repositoryGroupConfiguration.getRepositories() );
403 config.removeRepositoryGroup( repositoryGroupConfiguration );
404 repos.remove( repository.getId() );
405 repositoryGroupConfiguration.setRepositories( repos );
406 config.addRepositoryGroup( repositoryGroupConfiguration );
413 removeRepositoryRoles( repository );
415 catch ( RoleManagerException e )
417 throw new RepositoryAdminException(
418 "fail to remove repository roles for repository " + repository.getId() + " : " + e.getMessage(), e );
421 saveConfiguration( config );
427 public Boolean updateManagedRepository( ManagedRepository managedRepository, boolean needStageRepo,
428 AuditInformation auditInformation, boolean resetStats )
429 throws RepositoryAdminException
432 log.debug( "updateManagedConfiguration repo {} needStage {} resetStats {} ",
433 Arrays.asList( managedRepository, needStageRepo, resetStats ).toArray() );
435 // Ensure that the fields are valid.
437 getRepositoryCommonValidator().basicValidation( managedRepository, true );
439 Configuration configuration = getArchivaConfiguration().getConfiguration();
441 ManagedRepositoryConfiguration toremove = configuration.findManagedRepositoryById( managedRepository.getId() );
443 if ( toremove != null )
445 configuration.removeManagedRepository( toremove );
448 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( toremove );
450 // TODO remove content from old if path has changed !!!!!
452 if ( stagingRepository != null )
454 configuration.removeManagedRepository( stagingRepository );
457 ManagedRepositoryConfiguration managedRepositoryConfiguration =
458 addManagedRepository( managedRepository.getId(), managedRepository.getLayout(), managedRepository.getName(),
459 managedRepository.getLocation(), managedRepository.isBlockRedeployments(),
460 managedRepository.isReleases(), managedRepository.isSnapshots(), needStageRepo,
461 managedRepository.getCronExpression(), managedRepository.getIndexDirectory(),
462 managedRepository.getDaysOlder(), managedRepository.getRetentionCount(),
463 managedRepository.isDeleteReleasedSnapshots(), auditInformation,
464 getArchivaConfiguration().getConfiguration() );
466 // Save the repository configuration.
467 RepositorySession repositorySession = getRepositorySessionFactory().createSession();
471 triggerAuditEvent( managedRepositoryConfiguration.getId(), null, AuditEvent.MODIFY_MANAGED_REPO,
474 saveConfiguration( this.getArchivaConfiguration().getConfiguration() );
477 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
478 getRepositoryStatisticsManager().deleteStatistics( repositorySession.getRepository(),
479 managedRepositoryConfiguration.getId() );
480 repositorySession.save();
484 catch ( MetadataRepositoryException e )
486 throw new RepositoryAdminException( e.getMessage(), e );
490 repositorySession.close();
492 createIndexContext( managedRepository );
496 //--------------------------
498 //--------------------------
501 protected void addRepository( ManagedRepositoryConfiguration repository, Configuration configuration )
502 throws RepositoryAdminException, IOException
504 // Normalize the path
505 File file = new File( repository.getLocation() );
506 repository.setLocation( file.getCanonicalPath() );
507 if ( !file.exists() )
511 if ( !file.exists() || !file.isDirectory() )
513 throw new RepositoryAdminException(
514 "Unable to add repository - no write access, can not create the root directory: " + file );
517 configuration.addManagedRepository( repository );
521 public IndexingContext createIndexContext( ManagedRepository repository )
522 throws RepositoryAdminException
526 List<? extends IndexCreator> indexCreators = mavenIndexerUtils.getAllIndexCreators();
527 NexusIndexer indexer = plexusSisuBridge.lookup( NexusIndexer.class );
529 IndexingContext context = indexer.getIndexingContexts().get( repository.getId() );
531 if ( context != null )
533 log.debug( "skip adding repository with id {} as already exists", repository.getId() );
537 String indexDir = repository.getIndexDirectory();
538 File managedRepository = new File( repository.getLocation() );
540 File indexDirectory = null;
541 if ( indexDir != null && !"".equals( indexDir ) )
543 indexDirectory = new File( repository.getIndexDirectory() );
544 if ( !indexDirectory.isAbsolute() )
546 indexDirectory = new File( managedRepository, repository.getIndexDirectory() );
551 indexDirectory = new File( managedRepository, ".indexer" );
555 indexer.addIndexingContext( repository.getId(), repository.getId(), managedRepository, indexDirectory,
556 managedRepository.toURI().toURL().toExternalForm(),
557 indexDirectory.toURI().toURL().toString(), indexCreators );
559 context.setSearchable( repository.isScanned() );
562 catch ( MalformedURLException e )
564 throw new RepositoryAdminException( e.getMessage(), e );
566 catch ( IOException e )
568 throw new RepositoryAdminException( e.getMessage(), e );
570 catch ( PlexusSisuBridgeException e )
572 throw new RepositoryAdminException( e.getMessage(), e );
574 catch ( UnsupportedExistingLuceneIndexException e )
576 throw new RepositoryAdminException( e.getMessage(), e );
580 private ManagedRepositoryConfiguration getStageRepoConfig( ManagedRepositoryConfiguration repository )
582 ManagedRepositoryConfiguration stagingRepository = new ManagedRepositoryConfiguration();
583 stagingRepository.setId( repository.getId() + STAGE_REPO_ID_END );
584 stagingRepository.setLayout( repository.getLayout() );
585 stagingRepository.setName( repository.getName() + STAGE_REPO_ID_END );
586 stagingRepository.setBlockRedeployments( repository.isBlockRedeployments() );
587 stagingRepository.setDaysOlder( repository.getDaysOlder() );
588 stagingRepository.setDeleteReleasedSnapshots( repository.isDeleteReleasedSnapshots() );
589 stagingRepository.setIndexDir( repository.getIndexDir() );
590 String path = repository.getLocation();
591 int lastIndex = path.lastIndexOf( '/' );
592 stagingRepository.setLocation( path.substring( 0, lastIndex ) + "/" + stagingRepository.getId() );
593 stagingRepository.setRefreshCronExpression( repository.getRefreshCronExpression() );
594 stagingRepository.setReleases( repository.isReleases() );
595 stagingRepository.setRetentionCount( repository.getRetentionCount() );
596 stagingRepository.setScanned( repository.isScanned() );
597 stagingRepository.setSnapshots( repository.isSnapshots() );
598 return stagingRepository;
601 public Boolean scanRepository( String repositoryId, boolean fullScan )
603 if ( getRepositoryTaskScheduler().isProcessingRepositoryTask( repositoryId ) )
605 log.info( "scanning of repository with id {} already scheduled", repositoryId );
607 RepositoryTask task = new RepositoryTask();
608 task.setRepositoryId( repositoryId );
609 task.setScanAll( fullScan );
612 getRepositoryTaskScheduler().queueTask( task );
614 catch ( TaskQueueException e )
616 log.error( "failed to schedule scanning of repo with id {}", repositoryId, e );
622 protected void addRepositoryRoles( ManagedRepositoryConfiguration newRepository )
623 throws RoleManagerException
625 String repoId = newRepository.getId();
627 // TODO: double check these are configured on start up
628 // TODO: belongs in the business logic
630 if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
632 getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
635 if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
637 getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
641 protected void removeRepositoryRoles( ManagedRepositoryConfiguration existingRepository )
642 throws RoleManagerException
644 String repoId = existingRepository.getId();
646 if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
648 getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
651 if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
653 getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
656 log.debug( "removed user roles associated with repository {}", repoId );
659 //--------------------------
661 //--------------------------
664 public RoleManager getRoleManager()
669 public void setRoleManager( RoleManager roleManager )
671 this.roleManager = roleManager;
674 public RepositoryStatisticsManager getRepositoryStatisticsManager()
676 return repositoryStatisticsManager;
679 public void setRepositoryStatisticsManager( RepositoryStatisticsManager repositoryStatisticsManager )
681 this.repositoryStatisticsManager = repositoryStatisticsManager;
684 public RepositorySessionFactory getRepositorySessionFactory()
686 return repositorySessionFactory;
689 public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory )
691 this.repositorySessionFactory = repositorySessionFactory;
695 public RepositoryArchivaTaskScheduler getRepositoryTaskScheduler()
697 return repositoryTaskScheduler;
700 public void setRepositoryTaskScheduler( RepositoryArchivaTaskScheduler repositoryTaskScheduler )
702 this.repositoryTaskScheduler = repositoryTaskScheduler;