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;
187 private ManagedRepositoryConfiguration addManagedRepository( String repoId, String layout, String name,
188 String location, boolean blockRedeployments,
189 boolean releasesIncluded, boolean snapshotsIncluded,
190 boolean stageRepoNeeded, String cronExpression,
191 String indexDir, int daysOlder, int retentionCount,
192 boolean deteleReleasedSnapshots,
193 AuditInformation auditInformation,
194 Configuration config )
195 throws RepositoryAdminException
198 // FIXME : olamy can be empty to avoid scheduled scan ?
199 if ( StringUtils.isNotBlank( cronExpression ) )
201 CronExpressionValidator validator = new CronExpressionValidator();
203 if ( !validator.validate( cronExpression ) )
205 throw new RepositoryAdminException( "Invalid cron expression." );
210 throw new RepositoryAdminException( "Cron expression cannot be empty." );
213 String repoLocation = getRepositoryCommonValidator().removeExpressions( location );
215 if ( !GenericValidator.matchRegexp( repoLocation, REPOSITORY_LOCATION_VALID_EXPRESSION ) )
217 throw new RepositoryAdminException(
218 "Invalid repository location. Directory must only contain alphanumeric characters, equals(=), question-marks(?), "
219 + "exclamation-points(!), ampersands(&), forward-slashes(/), back-slashes(\\), underscores(_), dots(.), colons(:), tildes(~), and dashes(-)." );
222 ManagedRepositoryConfiguration repository = new ManagedRepositoryConfiguration();
224 repository.setId( repoId );
225 repository.setBlockRedeployments( blockRedeployments );
226 repository.setReleases( releasesIncluded );
227 repository.setSnapshots( snapshotsIncluded );
228 repository.setName( name );
229 repository.setLocation( repoLocation );
230 repository.setLayout( layout );
231 repository.setRefreshCronExpression( cronExpression );
232 repository.setIndexDir( indexDir );
233 repository.setDaysOlder( daysOlder );
234 repository.setRetentionCount( retentionCount );
235 repository.setDeleteReleasedSnapshots( deteleReleasedSnapshots );
236 repository.setIndexDir( indexDir );
240 addRepository( repository, config );
241 addRepositoryRoles( repository );
243 if ( stageRepoNeeded )
245 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( repository );
246 addRepository( stagingRepository, config );
247 addRepositoryRoles( stagingRepository );
248 triggerAuditEvent( stagingRepository.getId(), null, AuditEvent.ADD_MANAGED_REPO, auditInformation );
251 catch ( RoleManagerException e )
253 throw new RepositoryAdminException( "failed to add repository roles " + e.getMessage(), e );
255 catch ( IOException e )
257 throw new RepositoryAdminException( "failed to add repository " + e.getMessage(), e );
260 saveConfiguration( config );
262 //MRM-1342 Repository statistics report doesn't appear to be working correctly
263 //scan repository when adding of repository is successful
266 scanRepository( repoId, true );
267 // olamy no need of scanning staged repo
269 if ( stageRepoNeeded )
271 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( repository );
272 scanRepository( stagingRepository.getId(), true );
275 catch ( Exception e )
277 log.warn( new StringBuilder( "Unable to scan repository [" ).append( repoId ).append( "]: " ).append(
278 e.getMessage() ).toString(), e );
284 public Boolean deleteManagedRepository( String repositoryId, AuditInformation auditInformation,
285 boolean deleteContent )
286 throws RepositoryAdminException
288 Configuration config = getArchivaConfiguration().getConfiguration();
290 ManagedRepositoryConfiguration repository = config.findManagedRepositoryById( repositoryId );
292 if ( repository == null )
294 throw new RepositoryAdminException( "A repository with that id does not exist" );
297 triggerAuditEvent( repositoryId, null, AuditEvent.DELETE_MANAGED_REPO, auditInformation );
299 deleteManagedRepository( repository, deleteContent, config, false );
301 // stage repo exists ?
302 ManagedRepositoryConfiguration stagingRepository =
303 getArchivaConfiguration().getConfiguration().findManagedRepositoryById( repositoryId + STAGE_REPO_ID_END );
304 if ( stagingRepository != null )
306 // do not trigger event when deleting the staged one
307 deleteManagedRepository( stagingRepository, deleteContent, config, true );
312 saveConfiguration( config );
314 catch ( Exception e )
316 throw new RepositoryAdminException( "Error saving configuration for delete action" + e.getMessage() );
322 private Boolean deleteManagedRepository( ManagedRepositoryConfiguration repository, boolean deleteContent,
323 Configuration config, boolean stagedOne )
324 throws RepositoryAdminException
329 NexusIndexer nexusIndexer = plexusSisuBridge.lookup( NexusIndexer.class );
331 IndexingContext context = nexusIndexer.getIndexingContexts().get( repository.getId() );
332 if ( context != null )
334 nexusIndexer.removeIndexingContext( context, deleteContent );
337 catch ( PlexusSisuBridgeException e )
339 throw new RepositoryAdminException( e.getMessage(), e );
341 catch ( IOException e )
343 throw new RepositoryAdminException( e.getMessage(), e );
347 RepositorySession repositorySession = getRepositorySessionFactory().createSession();
350 MetadataRepository metadataRepository = repositorySession.getRepository();
351 metadataRepository.removeRepository( repository.getId() );
352 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
353 getRepositoryStatisticsManager().deleteStatistics( metadataRepository, repository.getId() );
354 repositorySession.save();
356 catch ( MetadataRepositoryException e )
358 throw new RepositoryAdminException( e.getMessage(), e );
362 repositorySession.close();
365 config.removeManagedRepository( repository );
369 // TODO could be async ? as directory can be huge
370 File dir = new File( repository.getLocation() );
371 if ( !FileUtils.deleteQuietly( dir ) )
373 throw new RepositoryAdminException( "Cannot delete repository " + dir );
377 // olamy: copy list for reading as a unit test in webapp fail with ConcurrentModificationException
378 List<ProxyConnectorConfiguration> proxyConnectors =
379 new ArrayList<ProxyConnectorConfiguration>( config.getProxyConnectors() );
380 for ( ProxyConnectorConfiguration proxyConnector : proxyConnectors )
382 if ( StringUtils.equals( proxyConnector.getSourceRepoId(), repository.getId() ) )
384 config.removeProxyConnector( proxyConnector );
388 Map<String, List<String>> repoToGroupMap = config.getRepositoryToGroupMap();
389 if ( repoToGroupMap != null )
391 if ( repoToGroupMap.containsKey( repository.getId() ) )
393 List<String> repoGroups = repoToGroupMap.get( repository.getId() );
394 for ( String repoGroup : repoGroups )
396 // copy to prevent UnsupportedOperationException
397 RepositoryGroupConfiguration repositoryGroupConfiguration =
398 config.findRepositoryGroupById( repoGroup );
399 List<String> repos = new ArrayList<String>( repositoryGroupConfiguration.getRepositories() );
400 config.removeRepositoryGroup( repositoryGroupConfiguration );
401 repos.remove( repository.getId() );
402 repositoryGroupConfiguration.setRepositories( repos );
403 config.addRepositoryGroup( repositoryGroupConfiguration );
410 removeRepositoryRoles( repository );
412 catch ( RoleManagerException e )
414 throw new RepositoryAdminException(
415 "fail to remove repository roles for repository " + repository.getId() + " : " + e.getMessage(), e );
418 saveConfiguration( config );
424 public Boolean updateManagedRepository( ManagedRepository managedRepository, boolean needStageRepo,
425 AuditInformation auditInformation, boolean resetStats )
426 throws RepositoryAdminException
429 log.debug( "updateManagedConfiguration repo {} needStage {} resetStats {} ",
430 Arrays.asList( managedRepository, needStageRepo, resetStats ).toArray() );
432 // Ensure that the fields are valid.
434 getRepositoryCommonValidator().basicValidation( managedRepository, true );
436 Configuration configuration = getArchivaConfiguration().getConfiguration();
438 ManagedRepositoryConfiguration toremove = configuration.findManagedRepositoryById( managedRepository.getId() );
440 if ( toremove != null )
442 configuration.removeManagedRepository( toremove );
445 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( toremove );
447 // TODO remove content from old if path has changed !!!!!
449 if ( stagingRepository != null )
451 configuration.removeManagedRepository( stagingRepository );
454 ManagedRepositoryConfiguration managedRepositoryConfiguration =
455 addManagedRepository( managedRepository.getId(), managedRepository.getLayout(), managedRepository.getName(),
456 managedRepository.getLocation(), managedRepository.isBlockRedeployments(),
457 managedRepository.isReleases(), managedRepository.isSnapshots(), needStageRepo,
458 managedRepository.getCronExpression(), managedRepository.getIndexDirectory(),
459 managedRepository.getDaysOlder(), managedRepository.getRetentionCount(),
460 managedRepository.isDeleteReleasedSnapshots(), auditInformation,
461 getArchivaConfiguration().getConfiguration() );
463 // Save the repository configuration.
464 RepositorySession repositorySession = getRepositorySessionFactory().createSession();
468 triggerAuditEvent( managedRepositoryConfiguration.getId(), null, AuditEvent.MODIFY_MANAGED_REPO,
471 saveConfiguration( this.getArchivaConfiguration().getConfiguration() );
474 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
475 getRepositoryStatisticsManager().deleteStatistics( repositorySession.getRepository(),
476 managedRepositoryConfiguration.getId() );
477 repositorySession.save();
481 catch ( MetadataRepositoryException e )
483 throw new RepositoryAdminException( e.getMessage(), e );
487 repositorySession.close();
493 //--------------------------
495 //--------------------------
498 protected void addRepository( ManagedRepositoryConfiguration repository, Configuration configuration )
499 throws RepositoryAdminException, IOException
501 // Normalize the path
502 File file = new File( repository.getLocation() );
503 repository.setLocation( file.getCanonicalPath() );
504 if ( !file.exists() )
508 if ( !file.exists() || !file.isDirectory() )
510 throw new RepositoryAdminException(
511 "Unable to add repository - no write access, can not create the root directory: " + file );
514 configuration.addManagedRepository( repository );
518 public IndexingContext createIndexContext( ManagedRepository repository )
519 throws RepositoryAdminException
523 List<? extends IndexCreator> indexCreators = mavenIndexerUtils.getAllIndexCreators();
524 NexusIndexer indexer = plexusSisuBridge.lookup( NexusIndexer.class );
526 IndexingContext context = indexer.getIndexingContexts().get( repository.getId() );
528 if ( context != null )
530 log.debug( "skip adding repository with id {} as already exists", repository.getId() );
534 String indexDir = repository.getIndexDirectory();
535 File managedRepository = new File( repository.getLocation() );
537 File indexDirectory = null;
538 if ( indexDir != null && !"".equals( indexDir ) )
540 indexDirectory = new File( repository.getIndexDirectory() );
544 indexDirectory = new File( managedRepository, ".indexer" );
548 indexer.addIndexingContext( repository.getId(), repository.getId(), managedRepository, indexDirectory,
549 managedRepository.toURI().toURL().toExternalForm(),
550 indexDirectory.toURI().toURL().toString(), indexCreators );
552 context.setSearchable( repository.isScanned() );
555 catch ( MalformedURLException e )
557 throw new RepositoryAdminException( e.getMessage(), e );
559 catch ( IOException e )
561 throw new RepositoryAdminException( e.getMessage(), e );
563 catch ( PlexusSisuBridgeException e )
565 throw new RepositoryAdminException( e.getMessage(), e );
567 catch ( UnsupportedExistingLuceneIndexException e )
569 throw new RepositoryAdminException( e.getMessage(), e );
573 private ManagedRepositoryConfiguration getStageRepoConfig( ManagedRepositoryConfiguration repository )
575 ManagedRepositoryConfiguration stagingRepository = new ManagedRepositoryConfiguration();
576 stagingRepository.setId( repository.getId() + STAGE_REPO_ID_END );
577 stagingRepository.setLayout( repository.getLayout() );
578 stagingRepository.setName( repository.getName() + STAGE_REPO_ID_END );
579 stagingRepository.setBlockRedeployments( repository.isBlockRedeployments() );
580 stagingRepository.setDaysOlder( repository.getDaysOlder() );
581 stagingRepository.setDeleteReleasedSnapshots( repository.isDeleteReleasedSnapshots() );
582 stagingRepository.setIndexDir( repository.getIndexDir() );
583 String path = repository.getLocation();
584 int lastIndex = path.lastIndexOf( '/' );
585 stagingRepository.setLocation( path.substring( 0, lastIndex ) + "/" + stagingRepository.getId() );
586 stagingRepository.setRefreshCronExpression( repository.getRefreshCronExpression() );
587 stagingRepository.setReleases( repository.isReleases() );
588 stagingRepository.setRetentionCount( repository.getRetentionCount() );
589 stagingRepository.setScanned( repository.isScanned() );
590 stagingRepository.setSnapshots( repository.isSnapshots() );
591 return stagingRepository;
594 public Boolean scanRepository( String repositoryId, boolean fullScan )
596 if ( getRepositoryTaskScheduler().isProcessingRepositoryTask( repositoryId ) )
598 log.info( "scanning of repository with id {} already scheduled", repositoryId );
600 RepositoryTask task = new RepositoryTask();
601 task.setRepositoryId( repositoryId );
602 task.setScanAll( fullScan );
605 getRepositoryTaskScheduler().queueTask( task );
607 catch ( TaskQueueException e )
609 log.error( "failed to schedule scanning of repo with id {}", repositoryId, e );
615 protected void addRepositoryRoles( ManagedRepositoryConfiguration newRepository )
616 throws RoleManagerException
618 String repoId = newRepository.getId();
620 // TODO: double check these are configured on start up
621 // TODO: belongs in the business logic
623 if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
625 getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
628 if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
630 getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
634 protected void removeRepositoryRoles( ManagedRepositoryConfiguration existingRepository )
635 throws RoleManagerException
637 String repoId = existingRepository.getId();
639 if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
641 getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
644 if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
646 getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
649 log.debug( "removed user roles associated with repository {}", repoId );
652 //--------------------------
654 //--------------------------
657 public RoleManager getRoleManager()
662 public void setRoleManager( RoleManager roleManager )
664 this.roleManager = roleManager;
667 public RepositoryStatisticsManager getRepositoryStatisticsManager()
669 return repositoryStatisticsManager;
672 public void setRepositoryStatisticsManager( RepositoryStatisticsManager repositoryStatisticsManager )
674 this.repositoryStatisticsManager = repositoryStatisticsManager;
677 public RepositorySessionFactory getRepositorySessionFactory()
679 return repositorySessionFactory;
682 public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory )
684 this.repositorySessionFactory = repositorySessionFactory;
688 public RepositoryArchivaTaskScheduler getRepositoryTaskScheduler()
690 return repositoryTaskScheduler;
693 public void setRepositoryTaskScheduler( RepositoryArchivaTaskScheduler repositoryTaskScheduler )
695 this.repositoryTaskScheduler = repositoryTaskScheduler;