1 package org.apache.maven.archiva.scheduled;
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
12 * http://www.apache.org/licenses/LICENSE-2.0
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
22 import org.apache.commons.collections.CollectionUtils;
23 import org.apache.maven.archiva.common.ArchivaException;
24 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
25 import org.apache.maven.archiva.configuration.ConfigurationEvent;
26 import org.apache.maven.archiva.configuration.ConfigurationListener;
27 import org.apache.maven.archiva.configuration.ManagedRepositoryConfiguration;
28 import org.apache.maven.archiva.scheduled.tasks.ArchivaTask;
29 import org.apache.maven.archiva.scheduled.tasks.DatabaseTask;
30 import org.apache.maven.archiva.scheduled.tasks.RepositoryTask;
31 import org.apache.maven.archiva.scheduled.tasks.RepositoryTaskSelectionPredicate;
32 import org.codehaus.plexus.personality.plexus.lifecycle.phase.Startable;
33 import org.codehaus.plexus.personality.plexus.lifecycle.phase.StartingException;
34 import org.codehaus.plexus.personality.plexus.lifecycle.phase.StoppingException;
35 import org.codehaus.plexus.scheduler.CronExpressionValidator;
36 import org.codehaus.plexus.scheduler.Scheduler;
37 import org.codehaus.plexus.taskqueue.Task;
38 import org.codehaus.plexus.taskqueue.TaskQueue;
39 import org.codehaus.plexus.taskqueue.TaskQueueException;
40 import org.codehaus.plexus.taskqueue.execution.TaskExecutionException;
41 import org.quartz.CronTrigger;
42 import org.quartz.JobDataMap;
43 import org.quartz.JobDetail;
44 import org.quartz.SchedulerException;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
48 import java.text.ParseException;
49 import java.util.HashSet;
50 import java.util.List;
54 * Default implementation of a scheduling component for archiva.
56 * @author <a href="mailto:brett@apache.org">Brett Porter</a>
57 * @author <a href="mailto:jmcconnell@apache.org">Jesse McConnell</a>
58 * @plexus.component role="org.apache.maven.archiva.scheduled.ArchivaTaskScheduler" role-hint="default"
60 public class DefaultArchivaTaskScheduler
61 implements ArchivaTaskScheduler, Startable, ConfigurationListener
63 private Logger log = LoggerFactory.getLogger( DefaultArchivaTaskScheduler.class );
68 private Scheduler scheduler;
71 * @plexus.requirement role-hint="database-update"
73 private TaskQueue databaseUpdateQueue;
76 * @plexus.requirement role-hint="repository-scanning"
78 private TaskQueue repositoryScanningQueue;
83 private ArchivaConfiguration archivaConfiguration;
85 public static final String DATABASE_SCAN_GROUP = "database-group";
87 public static final String DATABASE_JOB = "database-job";
89 public static final String DATABASE_JOB_TRIGGER = "database-job-trigger";
91 public static final String REPOSITORY_SCAN_GROUP = "repository-group";
93 public static final String REPOSITORY_JOB = "repository-job";
95 public static final String REPOSITORY_JOB_TRIGGER = "repository-job-trigger";
97 public static final String CRON_HOURLY = "0 0 * * * ?";
99 private Set<String> jobs = new HashSet<String>();
101 public void startup()
102 throws ArchivaException
104 archivaConfiguration.addListener( this );
110 catch ( StartingException e )
112 throw new ArchivaException( e.getMessage(), e );
117 throws StartingException
121 List<ManagedRepositoryConfiguration> repositories = archivaConfiguration.getConfiguration()
122 .getManagedRepositories();
124 for ( ManagedRepositoryConfiguration repoConfig : repositories )
126 if ( repoConfig.isScanned() )
128 scheduleRepositoryJobs( repoConfig );
132 scheduleDatabaseJobs();
134 catch ( SchedulerException e )
136 throw new StartingException( "Unable to start scheduler: " + e.getMessage(), e );
140 private synchronized void scheduleRepositoryJobs( ManagedRepositoryConfiguration repoConfig )
141 throws SchedulerException
143 if ( repoConfig.getRefreshCronExpression() == null )
145 log.warn( "Skipping job, no cron expression for " + repoConfig.getId() );
149 // get the cron string for these database scanning jobs
150 String cronString = repoConfig.getRefreshCronExpression();
152 CronExpressionValidator cronValidator = new CronExpressionValidator();
153 if ( !cronValidator.validate( cronString ) )
155 log.warn( "Cron expression [" + cronString + "] for repository [" + repoConfig.getId() +
156 "] is invalid. Defaulting to hourly." );
157 cronString = CRON_HOURLY;
160 // setup the unprocessed artifact job
161 JobDetail repositoryJob =
162 new JobDetail( REPOSITORY_JOB + ":" + repoConfig.getId(), REPOSITORY_SCAN_GROUP, RepositoryTaskJob.class );
164 JobDataMap dataMap = new JobDataMap();
165 dataMap.put( RepositoryTaskJob.TASK_QUEUE, repositoryScanningQueue );
166 dataMap.put( RepositoryTaskJob.TASK_QUEUE_POLICY, ArchivaTask.QUEUE_POLICY_WAIT );
167 dataMap.put( RepositoryTaskJob.TASK_REPOSITORY, repoConfig.getId() );
168 repositoryJob.setJobDataMap( dataMap );
172 CronTrigger trigger =
173 new CronTrigger( REPOSITORY_JOB_TRIGGER + ":" + repoConfig.getId(), REPOSITORY_SCAN_GROUP, cronString );
175 jobs.add( REPOSITORY_JOB + ":" + repoConfig.getId() );
176 scheduler.scheduleJob( repositoryJob, trigger );
178 catch ( ParseException e )
181 "ParseException in repository scanning cron expression, disabling repository scanning for '" +
182 repoConfig.getId() + "': " + e.getMessage() );
187 private synchronized void scheduleDatabaseJobs()
188 throws SchedulerException
190 String cronString = archivaConfiguration.getConfiguration().getDatabaseScanning().getCronExpression();
192 // setup the unprocessed artifact job
193 JobDetail databaseJob = new JobDetail( DATABASE_JOB, DATABASE_SCAN_GROUP, DatabaseTaskJob.class );
195 JobDataMap dataMap = new JobDataMap();
196 dataMap.put( DatabaseTaskJob.TASK_QUEUE, databaseUpdateQueue );
197 databaseJob.setJobDataMap( dataMap );
199 CronExpressionValidator cronValidator = new CronExpressionValidator();
200 if ( !cronValidator.validate( cronString ) )
203 "Cron expression [" + cronString + "] for database update is invalid. Defaulting to hourly." );
204 cronString = CRON_HOURLY;
209 CronTrigger trigger = new CronTrigger( DATABASE_JOB_TRIGGER, DATABASE_SCAN_GROUP, cronString );
211 scheduler.scheduleJob( databaseJob, trigger );
213 catch ( ParseException e )
216 "ParseException in database scanning cron expression, disabling database scanning: " + e.getMessage() );
222 throws StoppingException
226 scheduler.unscheduleJob( DATABASE_JOB, DATABASE_SCAN_GROUP );
228 for ( String job : jobs )
230 scheduler.unscheduleJob( job, REPOSITORY_SCAN_GROUP );
234 catch ( SchedulerException e )
236 throw new StoppingException( "Unable to unschedule tasks", e );
240 public void scheduleDatabaseTasks()
241 throws TaskExecutionException
245 scheduleDatabaseJobs();
247 catch ( SchedulerException e )
249 throw new TaskExecutionException( "Unable to schedule repository jobs: " + e.getMessage(), e );
254 public boolean isProcessingAnyRepositoryTask()
255 throws ArchivaException
257 List<? extends Task> queue = null;
261 queue = repositoryScanningQueue.getQueueSnapshot();
263 catch ( TaskQueueException e )
265 throw new ArchivaException( "Unable to get repository scanning queue:" + e.getMessage(), e );
268 return !queue.isEmpty();
271 public boolean isProcessingRepositoryTask( String repositoryId )
272 throws ArchivaException
274 List<? extends Task> queue = null;
278 queue = repositoryScanningQueue.getQueueSnapshot();
280 catch ( TaskQueueException e )
282 throw new ArchivaException( "Unable to get repository scanning queue:" + e.getMessage(), e );
285 return CollectionUtils.exists( queue, new RepositoryTaskSelectionPredicate( repositoryId ) );
288 public boolean isProcessingDatabaseTask()
289 throws ArchivaException
291 List<? extends Task> queue = null;
295 queue = databaseUpdateQueue.getQueueSnapshot();
297 catch ( TaskQueueException e )
299 throw new ArchivaException( "Unable to get database update queue:" + e.getMessage(), e );
302 return !queue.isEmpty();
305 public void queueRepositoryTask( RepositoryTask task )
306 throws TaskQueueException
308 repositoryScanningQueue.put( task );
311 public void queueDatabaseTask( DatabaseTask task )
312 throws TaskQueueException
314 databaseUpdateQueue.put( task );
317 public void configurationEvent( ConfigurationEvent event )
319 if ( event.getType() == ConfigurationEvent.SAVED )
323 scheduler.unscheduleJob( DATABASE_JOB, DATABASE_SCAN_GROUP );
325 scheduleDatabaseJobs();
327 catch ( SchedulerException e )
329 log.error( "Error restarting the database scanning job after property change." );
332 for ( String job : jobs )
336 scheduler.unscheduleJob( job, REPOSITORY_SCAN_GROUP );
338 catch ( SchedulerException e )
340 log.error( "Error restarting the repository scanning job after property change." );
345 List<ManagedRepositoryConfiguration> repositories = archivaConfiguration.getConfiguration()
346 .getManagedRepositories();
348 for ( ManagedRepositoryConfiguration repoConfig : repositories )
350 if ( repoConfig.getRefreshCronExpression() != null )
354 scheduleRepositoryJobs( repoConfig );
356 catch ( SchedulerException e )
358 log.error( "error restarting job: " + REPOSITORY_JOB + ":" + repoConfig.getId() );