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 ) != null;
159 private ManagedRepositoryConfiguration addManagedRepository( String repoId, String layout, String name,
160 String location, boolean blockRedeployments,
161 boolean releasesIncluded, boolean snapshotsIncluded,
162 boolean stageRepoNeeded, String cronExpression,
163 String indexDir, int daysOlder, int retentionCount,
164 boolean deteleReleasedSnapshots,
165 AuditInformation auditInformation )
166 throws RepositoryAdminException
169 Configuration config = getArchivaConfiguration().getConfiguration();
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 = config.findRepositoryGroupById( repoGroup );
353 List<String> repos = new ArrayList<String>( repositoryGroupConfiguration.getRepositories() );
354 config.removeRepositoryGroup( repositoryGroupConfiguration );
355 repos.remove( repository.getId() );
356 repositoryGroupConfiguration.setRepositories( repos );
357 config.addRepositoryGroup( repositoryGroupConfiguration );
364 removeRepositoryRoles( repository );
366 catch ( RoleManagerException e )
368 throw new RepositoryAdminException(
369 "fail to remove repository roles for repository " + repository.getId() + " : " + e.getMessage(), e );
372 saveConfiguration( config );
378 public Boolean updateManagedRepository( ManagedRepository managedRepository, boolean needStageRepo,
379 AuditInformation auditInformation, boolean resetStats )
380 throws RepositoryAdminException
383 log.debug( "updateManagedConfiguration repo {} needStage {} resetStats {} ",
384 Arrays.asList( managedRepository, needStageRepo, resetStats ).toArray() );
386 // Ensure that the fields are valid.
388 getRepositoryCommonValidator().basicValidation( managedRepository, true );
390 Configuration configuration = getArchivaConfiguration().getConfiguration();
392 ManagedRepositoryConfiguration toremove = configuration.findManagedRepositoryById( managedRepository.getId() );
394 if ( toremove != null )
396 configuration.removeManagedRepository( toremove );
399 ManagedRepositoryConfiguration stagingRepository = getStageRepoConfig( toremove );
401 // TODO remove content from old if path has changed !!!!!
403 if ( stagingRepository != null )
405 configuration.removeManagedRepository( stagingRepository );
408 if ( toremove != null && stagingRepository != null )
410 saveConfiguration( configuration );
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 );
421 // Save the repository configuration.
422 RepositorySession repositorySession = getRepositorySessionFactory().createSession();
426 triggerAuditEvent( managedRepositoryConfiguration.getId(), null, AuditEvent.MODIFY_MANAGED_REPO,
429 saveConfiguration( this.getArchivaConfiguration().getConfiguration() );
432 log.debug( "call repositoryStatisticsManager.deleteStatistics" );
433 getRepositoryStatisticsManager().deleteStatistics( repositorySession.getRepository(),
434 managedRepositoryConfiguration.getId() );
435 repositorySession.save();
439 catch ( MetadataRepositoryException e )
441 throw new RepositoryAdminException( e.getMessage(), e );
445 repositorySession.close();
451 //--------------------------
453 //--------------------------
456 protected void addRepository( ManagedRepositoryConfiguration repository, Configuration configuration )
457 throws RepositoryAdminException, IOException
459 // Normalize the path
460 File file = new File( repository.getLocation() );
461 repository.setLocation( file.getCanonicalPath() );
462 if ( !file.exists() )
466 if ( !file.exists() || !file.isDirectory() )
468 throw new RepositoryAdminException(
469 "Unable to add repository - no write access, can not create the root directory: " + file );
472 configuration.addManagedRepository( repository );
475 private ManagedRepositoryConfiguration getStageRepoConfig( ManagedRepositoryConfiguration repository )
477 ManagedRepositoryConfiguration stagingRepository = new ManagedRepositoryConfiguration();
478 stagingRepository.setId( repository.getId() + STAGE_REPO_ID_END );
479 stagingRepository.setLayout( repository.getLayout() );
480 stagingRepository.setName( repository.getName() + STAGE_REPO_ID_END );
481 stagingRepository.setBlockRedeployments( repository.isBlockRedeployments() );
482 stagingRepository.setDaysOlder( repository.getDaysOlder() );
483 stagingRepository.setDeleteReleasedSnapshots( repository.isDeleteReleasedSnapshots() );
484 stagingRepository.setIndexDir( repository.getIndexDir() );
485 String path = repository.getLocation();
486 int lastIndex = path.lastIndexOf( '/' );
487 stagingRepository.setLocation( path.substring( 0, lastIndex ) + "/" + stagingRepository.getId() );
488 stagingRepository.setRefreshCronExpression( repository.getRefreshCronExpression() );
489 stagingRepository.setReleases( repository.isReleases() );
490 stagingRepository.setRetentionCount( repository.getRetentionCount() );
491 stagingRepository.setScanned( repository.isScanned() );
492 stagingRepository.setSnapshots( repository.isSnapshots() );
493 return stagingRepository;
496 public Boolean scanRepository( String repositoryId, boolean fullScan )
498 if ( getRepositoryTaskScheduler().isProcessingRepositoryTask( repositoryId ) )
500 log.info( "scanning of repository with id {} already scheduled", repositoryId );
502 RepositoryTask task = new RepositoryTask();
503 task.setRepositoryId( repositoryId );
504 task.setScanAll( fullScan );
507 getRepositoryTaskScheduler().queueTask( task );
509 catch ( TaskQueueException e )
511 log.error( "failed to schedule scanning of repo with id {}", repositoryId, e );
517 protected void addRepositoryRoles( ManagedRepositoryConfiguration newRepository )
518 throws RoleManagerException
520 String repoId = newRepository.getId();
522 // TODO: double check these are configured on start up
523 // TODO: belongs in the business logic
525 if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
527 getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
530 if ( !getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
532 getRoleManager().createTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
536 protected void removeRepositoryRoles( ManagedRepositoryConfiguration existingRepository )
537 throws RoleManagerException
539 String repoId = existingRepository.getId();
541 if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId ) )
543 getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_MANAGER, repoId );
546 if ( getRoleManager().templatedRoleExists( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId ) )
548 getRoleManager().removeTemplatedRole( ArchivaRoleConstants.TEMPLATE_REPOSITORY_OBSERVER, repoId );
551 log.debug( "removed user roles associated with repository {}", repoId );
554 //--------------------------
556 //--------------------------
559 public RoleManager getRoleManager()
564 public void setRoleManager( RoleManager roleManager )
566 this.roleManager = roleManager;
569 public RepositoryStatisticsManager getRepositoryStatisticsManager()
571 return repositoryStatisticsManager;
574 public void setRepositoryStatisticsManager( RepositoryStatisticsManager repositoryStatisticsManager )
576 this.repositoryStatisticsManager = repositoryStatisticsManager;
579 public RepositorySessionFactory getRepositorySessionFactory()
581 return repositorySessionFactory;
584 public void setRepositorySessionFactory( RepositorySessionFactory repositorySessionFactory )
586 this.repositorySessionFactory = repositorySessionFactory;
590 public RepositoryArchivaTaskScheduler getRepositoryTaskScheduler()
592 return repositoryTaskScheduler;
595 public void setRepositoryTaskScheduler( RepositoryArchivaTaskScheduler repositoryTaskScheduler )
597 this.repositoryTaskScheduler = repositoryTaskScheduler;