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.AuditInformation;
22 import org.apache.archiva.admin.repository.AbstractRepositoryAdmin;
23 import org.apache.archiva.admin.repository.RepositoryAdminException;
24 import org.apache.archiva.audit.AuditEvent;
25 import org.apache.archiva.metadata.repository.MetadataRepository;
26 import org.apache.archiva.metadata.repository.MetadataRepositoryException;
27 import org.apache.archiva.metadata.repository.RepositorySession;
28 import org.apache.archiva.metadata.repository.RepositorySessionFactory;
29 import org.apache.archiva.metadata.repository.stats.RepositoryStatisticsManager;
30 import org.apache.archiva.scheduler.repository.RepositoryArchivaTaskScheduler;
31 import org.apache.archiva.scheduler.repository.RepositoryTask;
32 import org.apache.archiva.security.common.ArchivaRoleConstants;
33 import org.apache.commons.io.FileUtils;
34 import org.apache.commons.lang.StringUtils;
35 import org.apache.commons.validator.GenericValidator;
36 import org.apache.maven.archiva.configuration.Configuration;
37 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
38 import org.apache.maven.archiva.configuration.ProxyConnectorConfiguration;
39 import org.apache.maven.archiva.configuration.RepositoryGroupConfiguration;
40 import org.codehaus.plexus.redback.role.RoleManager;
41 import org.codehaus.plexus.redback.role.RoleManagerException;
42 import org.codehaus.plexus.taskqueue.TaskQueueException;
43 import org.codehaus.redback.components.scheduler.CronExpressionValidator;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46 import org.springframework.stereotype.Service;
48 import javax.inject.Inject;
49 import javax.inject.Named;
51 import java.io.IOException;
52 import java.util.ArrayList;
53 import java.util.Arrays;
54 import java.util.HashMap;
55 import java.util.List;
59 * FIXME remove all generic Exception to have usefull ones
60 * FIXME review the staging mechanism to have a per user session one
62 * @author Olivier Lamy
64 @Service( "managedRepositoryAdmin#default" )
65 public class DefaultManagedRepositoryAdmin
66 extends AbstractRepositoryAdmin
67 implements ManagedRepositoryAdmin
70 public static final String REPOSITORY_LOCATION_VALID_EXPRESSION = "^[-a-zA-Z0-9._/~:?!&=\\\\]+$";
72 private Logger log = LoggerFactory.getLogger( getClass() );
74 public static final String STAGE_REPO_ID_END = "-stage";
78 @Named( value = "archivaTaskScheduler#repository" )
79 private RepositoryArchivaTaskScheduler repositoryTaskScheduler;
82 private RepositorySessionFactory repositorySessionFactory;
85 private RepositoryStatisticsManager repositoryStatisticsManager;
89 protected RoleManager roleManager;
91 public List<ManagedRepository> getManagedRepositories()
92 throws RepositoryAdminException
94 List<ManagedRepositoryConfiguration> managedRepoConfigs =
95 getArchivaConfiguration().getConfiguration().getManagedRepositories();
97 List<ManagedRepository> managedRepos = new ArrayList<ManagedRepository>( managedRepoConfigs.size() );
99 for ( ManagedRepositoryConfiguration repoConfig : managedRepoConfigs )
101 // TODO add staging repo information back too
102 ManagedRepository repo =
103 new ManagedRepository( repoConfig.getId(), repoConfig.getName(), repoConfig.getLocation(),
104 repoConfig.getLayout(), repoConfig.isSnapshots(), repoConfig.isReleases(),
105 repoConfig.isBlockRedeployments(), repoConfig.getRefreshCronExpression(),
106 repoConfig.getIndexDir(), repoConfig.isScanned(), repoConfig.getDaysOlder(),
107 repoConfig.getRetentionCount(), repoConfig.isDeleteReleasedSnapshots() );
109 managedRepos.add( repo );
115 public Map<String, ManagedRepository> getManagedRepositoriesAsMap()
116 throws RepositoryAdminException
118 List<ManagedRepository> managedRepositories = getManagedRepositories();
119 Map<String, ManagedRepository> repositoriesMap =
120 new HashMap<String, ManagedRepository>( managedRepositories.size() );
121 for ( ManagedRepository managedRepository : managedRepositories )
123 repositoriesMap.put( managedRepository.getId(), managedRepository );
125 return repositoriesMap;
128 public ManagedRepository getManagedRepository( String repositoryId )
129 throws RepositoryAdminException
131 List<ManagedRepository> repos = getManagedRepositories();
132 for ( ManagedRepository repo : repos )
134 if ( StringUtils.equals( repo.getId(), repositoryId ) )
142 public Boolean addManagedRepository( ManagedRepository managedRepository, boolean needStageRepo,
143 AuditInformation auditInformation )
144 throws RepositoryAdminException
147 getRepositoryCommonValidator().basicValidation( managedRepository, false );
148 triggerAuditEvent( managedRepository.getId(), null, AuditEvent.ADD_MANAGED_REPO, auditInformation );
150 addManagedRepository( managedRepository.getId(), managedRepository.getLayout(), managedRepository.getName(),
151 managedRepository.getLocation(), managedRepository.isBlockRedeployments(),
152 managedRepository.isReleases(), managedRepository.isSnapshots(), needStageRepo,
153 managedRepository.getCronExpression(), managedRepository.getIndexDirectory(),
154 managedRepository.getDaysOlder(), managedRepository.getRetentionCount(),
155 managedRepository.isDeleteReleasedSnapshots(), auditInformation,
156 getArchivaConfiguration().getConfiguration() ) != null;
160 private ManagedRepositoryConfiguration addManagedRepository( String repoId, String layout, String name,
161 String location, boolean blockRedeployments,
162 boolean releasesIncluded, boolean snapshotsIncluded,
163 boolean stageRepoNeeded, String cronExpression,
164 String indexDir, int daysOlder, int retentionCount,
165 boolean deteleReleasedSnapshots,
166 AuditInformation auditInformation,
167 Configuration config )
168 throws RepositoryAdminException
171 // FIXME : olamy can be empty to avoid scheduled scan ?
172 if ( StringUtils.isNotBlank( cronExpression ) )
174 CronExpressionValidator validator = new CronExpressionValidator();
176 if ( !validator.validate( cronExpression ) )
178 throw new RepositoryAdminException( "Invalid cron expression." );
183 throw new RepositoryAdminException( "Cron expression cannot be empty." );
186 String repoLocation = getRepositoryCommonValidator().removeExpressions( location );
188 if ( !GenericValidator.matchRegexp( repoLocation, REPOSITORY_LOCATION_VALID_EXPRESSION ) )
190 throw new RepositoryAdminException(
191 "Invalid repository location. Directory must only contain alphanumeric characters, equals(=), question-marks(?), "
192 + "exclamation-points(!), ampersands(&), forward-slashes(/), back-slashes(\\), underscores(_), dots(.), colons(:), tildes(~), and dashes(-)." );
195 ManagedRepositoryConfiguration repository = new ManagedRepositoryConfiguration();
197 repository.setId( repoId );
198 repository.setBlockRedeployments( blockRedeployments );
199 repository.setReleases( releasesIncluded );
200 repository.setSnapshots( snapshotsIncluded );
201 repository.setName( name );
202 repository.setLocation( repoLocation );
203 repository.setLayout( layout );
204 repository.setRefreshCronExpression( cronExpression );
205 repository.setIndexDir( indexDir );
206 repository.setDaysOlder( daysOlder );
207 repository.setRetentionCount( retentionCount );
208 repository.setDeleteReleasedSnapshots( deteleReleasedSnapshots );
211 addRepository( repository, config );
212 addRepositoryRoles( repository );
214 if ( stageRepoNeeded )
216 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( repository );
217 addRepository( stagingRepository, config );
218 addRepositoryRoles( stagingRepository );
219 triggerAuditEvent( stagingRepository.getId(), null, AuditEvent.ADD_MANAGED_REPO, auditInformation );
222 catch ( RoleManagerException e )
224 throw new RepositoryAdminException( "failed to add repository roles " + e.getMessage(), e );
226 catch ( IOException e )
228 throw new RepositoryAdminException( "failed to add repository " + e.getMessage(), e );
231 saveConfiguration( config );
233 //MRM-1342 Repository statistics report doesn't appear to be working correctly
234 //scan repository when adding of repository is successful
237 scanRepository( repoId, true );
238 // olamy no need of scanning staged repo
240 if ( stageRepoNeeded )
242 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( repository );
243 scanRepository( stagingRepository.getId(), true );
246 catch ( Exception e )
248 log.warn( new StringBuilder( "Unable to scan repository [" ).append( repoId ).append( "]: " ).append(
249 e.getMessage() ).toString(), e );
256 // FIXME cleanup repositoryGroups when deleting a ManagedRepo
257 public Boolean deleteManagedRepository( String repositoryId, AuditInformation auditInformation,
258 boolean deleteContent )
259 throws RepositoryAdminException
261 Configuration config = getArchivaConfiguration().getConfiguration();
263 ManagedRepositoryConfiguration repository = config.findManagedRepositoryById( repositoryId );
265 if ( repository == null )
267 throw new RepositoryAdminException( "A repository with that id does not exist" );
270 triggerAuditEvent( repositoryId, null, AuditEvent.DELETE_MANAGED_REPO, auditInformation );
272 deleteManagedRepository( repository, deleteContent, config, false );
274 // stage repo exists ?
275 ManagedRepositoryConfiguration stagingRepository =
276 getArchivaConfiguration().getConfiguration().findManagedRepositoryById( repositoryId + STAGE_REPO_ID_END );
277 if ( stagingRepository != null )
279 // do not trigger event when deleting the staged one
280 //triggerAuditEvent( stagingRepository.getId(), null, AuditEvent.DELETE_MANAGED_REPO, auditInformation );
281 deleteManagedRepository( stagingRepository, deleteContent, config, true );
286 saveConfiguration( config );
288 catch ( Exception e )
290 throw new RepositoryAdminException( "Error saving configuration for delete action" + e.getMessage() );
296 private Boolean deleteManagedRepository( ManagedRepositoryConfiguration repository, boolean deleteContent,
297 Configuration config, boolean stagedOne )
298 throws RepositoryAdminException
302 RepositorySession repositorySession = getRepositorySessionFactory().createSession();
305 MetadataRepository metadataRepository = repositorySession.getRepository();
306 metadataRepository.removeRepository( repository.getId() );
307 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
308 getRepositoryStatisticsManager().deleteStatistics( metadataRepository, repository.getId() );
309 repositorySession.save();
311 catch ( MetadataRepositoryException e )
313 throw new RepositoryAdminException( e.getMessage(), e );
317 repositorySession.close();
320 config.removeManagedRepository( repository );
324 // TODO could be async ? as directory can be huge
325 File dir = new File( repository.getLocation() );
326 if ( !FileUtils.deleteQuietly( dir ) )
328 throw new RepositoryAdminException( "Cannot delete repository " + dir );
332 // olamy: copy list for reading as a unit test in webapp fail with ConcurrentModificationException
333 List<ProxyConnectorConfiguration> proxyConnectors =
334 new ArrayList<ProxyConnectorConfiguration>( config.getProxyConnectors() );
335 for ( ProxyConnectorConfiguration proxyConnector : proxyConnectors )
337 if ( StringUtils.equals( proxyConnector.getSourceRepoId(), repository.getId() ) )
339 config.removeProxyConnector( proxyConnector );
343 Map<String, List<String>> repoToGroupMap = config.getRepositoryToGroupMap();
344 if ( repoToGroupMap != null )
346 if ( repoToGroupMap.containsKey( repository.getId() ) )
348 List<String> repoGroups = repoToGroupMap.get( repository.getId() );
349 for ( String repoGroup : repoGroups )
351 // copy to prevent UnsupportedOperationException
352 RepositoryGroupConfiguration repositoryGroupConfiguration =
353 config.findRepositoryGroupById( repoGroup );
354 List<String> repos = new ArrayList<String>( repositoryGroupConfiguration.getRepositories() );
355 config.removeRepositoryGroup( repositoryGroupConfiguration );
356 repos.remove( repository.getId() );
357 repositoryGroupConfiguration.setRepositories( repos );
358 config.addRepositoryGroup( repositoryGroupConfiguration );
365 removeRepositoryRoles( repository );
367 catch ( RoleManagerException e )
369 throw new RepositoryAdminException(
370 "fail to remove repository roles for repository " + repository.getId() + " : " + e.getMessage(), e );
373 saveConfiguration( config );
379 public Boolean updateManagedRepository( ManagedRepository managedRepository, boolean needStageRepo,
380 AuditInformation auditInformation, boolean resetStats )
381 throws RepositoryAdminException
384 log.debug( "updateManagedConfiguration repo {} needStage {} resetStats {} ",
385 Arrays.asList( managedRepository, needStageRepo, resetStats ).toArray() );
387 // Ensure that the fields are valid.
389 getRepositoryCommonValidator().basicValidation( managedRepository, true );
391 Configuration configuration = getArchivaConfiguration().getConfiguration();
393 ManagedRepositoryConfiguration toremove = configuration.findManagedRepositoryById( managedRepository.getId() );
395 if ( toremove != null )
397 configuration.removeManagedRepository( toremove );
400 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( toremove );
402 // TODO remove content from old if path has changed !!!!!
404 if ( stagingRepository != null )
406 configuration.removeManagedRepository( stagingRepository );
409 ManagedRepositoryConfiguration managedRepositoryConfiguration =
410 addManagedRepository( managedRepository.getId(), managedRepository.getLayout(), managedRepository.getName(),
411 managedRepository.getLocation(), managedRepository.isBlockRedeployments(),
412 managedRepository.isReleases(), managedRepository.isSnapshots(), needStageRepo,
413 managedRepository.getCronExpression(), managedRepository.getIndexDirectory(),
414 managedRepository.getDaysOlder(), managedRepository.getRetentionCount(),
415 managedRepository.isDeleteReleasedSnapshots(), auditInformation,
416 getArchivaConfiguration().getConfiguration() );
418 // Save the repository configuration.
419 RepositorySession repositorySession = getRepositorySessionFactory().createSession();
423 triggerAuditEvent( managedRepositoryConfiguration.getId(), null, AuditEvent.MODIFY_MANAGED_REPO,
426 saveConfiguration( this.getArchivaConfiguration().getConfiguration() );
429 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
430 getRepositoryStatisticsManager().deleteStatistics( repositorySession.getRepository(),
431 managedRepositoryConfiguration.getId() );
432 repositorySession.save();
436 catch ( MetadataRepositoryException e )
438 throw new RepositoryAdminException( e.getMessage(), e );
442 repositorySession.close();
448 //--------------------------
450 //--------------------------
453 protected void addRepository( ManagedRepositoryConfiguration repository, Configuration configuration )
454 throws RepositoryAdminException, IOException
456 // Normalize the path
457 File file = new File( repository.getLocation() );
458 repository.setLocation( file.getCanonicalPath() );
459 if ( !file.exists() )
463 if ( !file.exists() || !file.isDirectory() )
465 throw new RepositoryAdminException(
466 "Unable to add repository - no write access, can not create the root directory: " + file );
469 configuration.addManagedRepository( repository );
472 private ManagedRepositoryConfiguration getStageRepoConfig( ManagedRepositoryConfiguration repository )
474 ManagedRepositoryConfiguration stagingRepository = new ManagedRepositoryConfiguration();
475 stagingRepository.setId( repository.getId() + STAGE_REPO_ID_END );
476 stagingRepository.setLayout( repository.getLayout() );
477 stagingRepository.setName( repository.getName() + STAGE_REPO_ID_END );
478 stagingRepository.setBlockRedeployments( repository.isBlockRedeployments() );
479 stagingRepository.setDaysOlder( repository.getDaysOlder() );
480 stagingRepository.setDeleteReleasedSnapshots( repository.isDeleteReleasedSnapshots() );
481 stagingRepository.setIndexDir( repository.getIndexDir() );
482 String path = repository.getLocation();
483 int lastIndex = path.lastIndexOf( '/' );
484 stagingRepository.setLocation( path.substring( 0, lastIndex ) + "/" + stagingRepository.getId() );
485 stagingRepository.setRefreshCronExpression( repository.getRefreshCronExpression() );
486 stagingRepository.setReleases( repository.isReleases() );
487 stagingRepository.setRetentionCount( repository.getRetentionCount() );
488 stagingRepository.setScanned( repository.isScanned() );
489 stagingRepository.setSnapshots( repository.isSnapshots() );
490 return stagingRepository;
493 public Boolean scanRepository( String repositoryId, boolean fullScan )
495 if ( getRepositoryTaskScheduler().isProcessingRepositoryTask( repositoryId ) )
497 log.info( "scanning of repository with id {} already scheduled", repositoryId );
499 RepositoryTask task = new RepositoryTask();
500 task.setRepositoryId( repositoryId );
501 task.setScanAll( fullScan );
504 getRepositoryTaskScheduler().queueTask( task );
506 catch ( TaskQueueException e )
508 log.error( "failed to schedule scanning of repo with id {}", repositoryId, e );
514 protected void addRepositoryRoles( ManagedRepositoryConfiguration newRepository )
515 throws RoleManagerException
517 String repoId = newRepository.getId();
519 // TODO: double check these are configured on start up
520 // TODO: belongs in the business logic
522 if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
524 getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
527 if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
529 getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
533 protected void removeRepositoryRoles( ManagedRepositoryConfiguration existingRepository )
534 throws RoleManagerException
536 String repoId = existingRepository.getId();
538 if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
540 getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
543 if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
545 getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
548 log.debug( "removed user roles associated with repository {}", repoId );
551 //--------------------------
553 //--------------------------
556 public RoleManager getRoleManager()
561 public void setRoleManager( RoleManager roleManager )
563 this.roleManager = roleManager;
566 public RepositoryStatisticsManager getRepositoryStatisticsManager()
568 return repositoryStatisticsManager;
571 public void setRepositoryStatisticsManager( RepositoryStatisticsManager repositoryStatisticsManager )
573 this.repositoryStatisticsManager = repositoryStatisticsManager;
576 public RepositorySessionFactory getRepositorySessionFactory()
578 return repositorySessionFactory;
581 public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory )
583 this.repositorySessionFactory = repositorySessionFactory;
587 public RepositoryArchivaTaskScheduler getRepositoryTaskScheduler()
589 return repositoryTaskScheduler;
592 public void setRepositoryTaskScheduler( RepositoryArchivaTaskScheduler repositoryTaskScheduler )
594 this.repositoryTaskScheduler = repositoryTaskScheduler;