/*
* This program is copyright (c) 2007 Hortis-GRC SA.
*
* This file is part of Sonar.
* Sonar is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Sonar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Sonar; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
package ch.hortis.sonar.core;
import ch.hortis.sonar.model.JdbcData;
import org.quartz.JobDetail;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.sql.Connection;
import java.util.Date;
import java.util.Properties;
public class Batch {
public static final long DEFAULT_PURGE_REPEAT_INTERVAL_MILLIS = 24 * 60 * 60 * 1000; // 24h
private long purgeRepeatInMillis = DEFAULT_PURGE_REPEAT_INTERVAL_MILLIS;
public final static String SONAR_JOBS_GROUP = "Sonar jobs";
public final static String METRICS_CALCULATIONS_JOB_NAME = "Metrics calculations job";
public final static String DATA_CLEANUP_JOB_NAME = "Data cleanup job";
private BaseJobDetail calculatorJobDetail;
private Scheduler scheduler;
private JdbcData jdbcData;
protected static final Logger LOG = LoggerFactory.getLogger( Batch.class );
private static Batch systemInstance = null;
public Batch( String jdbcURL, String jdbcDriver, String username, String password ) {
this( new JdbcData( jdbcURL, jdbcDriver, username, password ) );
}
public Batch( String datasource, String jdbcDialect ) {
this( new JdbcData( datasource, jdbcDialect ) );
}
public Batch( JdbcData jdbcData ) {
this.jdbcData = jdbcData;
if ( systemInstance == null ) {
systemInstance = this;
}
}
public static Batch getSystemInstance() {
return systemInstance;
}
public long getPurgeRepeatInMillis() {
return purgeRepeatInMillis;
}
public void setPurgeRepeatInMillis( long purgeRepeatInMillis ) {
this.purgeRepeatInMillis = purgeRepeatInMillis;
}
public void start() throws SchedulerException {
Properties props = new Properties();
props.setProperty( "org.quartz.scheduler.instanceName", "sonar-scheduler" );
props.setProperty( "org.quartz.threadPool.class", org.quartz.simpl.SimpleThreadPool.class.getName() );
props.setProperty( "org.quartz.threadPool.threadCount", "1" );
props.setProperty( "org.quartz.jobStore.class", org.quartz.simpl.RAMJobStore.class.getName() );
StdSchedulerFactory factory = new StdSchedulerFactory();
factory.initialize( props );
scheduler = factory.getScheduler();
Date firstStartup = new Date( System.currentTimeMillis() + ( 30 * 1000 ) );
jdbcData.setIsolationLevel( Connection.TRANSACTION_READ_COMMITTED );
calculatorJobDetail = new BaseJobDetail( METRICS_CALCULATIONS_JOB_NAME, SONAR_JOBS_GROUP, MeasuresCalculatorJob.class );
calculatorJobDetail.setJdbcData( jdbcData );
JobsChaining.chainJob( ProjectEreaseJob.class );
JobsChaining.chainJob( SnapshotGroupsPurgeJob.class );
JobsChaining.chainJob( MeasuresPurgeJob.class );
BaseJobDetail dataCleanupDetail = new BaseJobDetail( DATA_CLEANUP_JOB_NAME, SONAR_JOBS_GROUP, JobsChaining.class );
dataCleanupDetail.setJdbcData( jdbcData );
Trigger dataCleanupTrigger = new SimpleTrigger( DATA_CLEANUP_JOB_NAME, SONAR_JOBS_GROUP, firstStartup, null,
SimpleTrigger.REPEAT_INDEFINITELY, purgeRepeatInMillis );
scheduler.scheduleJob( dataCleanupDetail, dataCleanupTrigger );
LOG.info( "Scheduling job " + JobsChaining.class.getName() + " for each " + purgeRepeatInMillis + " ms" );
scheduler.addJobListener( new RunningJobListener( METRICS_CALCULATIONS_JOB_NAME ) );
scheduler.start();
LOG.info( "Scheduler started" );
}
public void shutdown() throws SchedulerException {
boolean waitJobsToComplete = true;
scheduler.shutdown( waitJobsToComplete );
LOG.info( "Scheduler stopped" );
}
public synchronized void triggerJob( String jobName ) throws SchedulerException {
if ( jobName.equals( METRICS_CALCULATIONS_JOB_NAME ) ) {
Trigger calculationsTrigger = new SimpleTrigger( jobName, SONAR_JOBS_GROUP, new Date() );
RunningJobListener listener = (RunningJobListener)scheduler.getJobListener( jobName );
// scheduling a job currently running, enqueue it later trough the listener
if ( listener.isJobRunning() ) {
listener.scheduleJob( calculatorJobDetail, calculationsTrigger );
} else {
scheduler.scheduleJob( calculatorJobDetail, calculationsTrigger );
}
} else {
scheduler.triggerJob( jobName, Batch.SONAR_JOBS_GROUP );
}
}
private class RunningJobListener implements JobListener {
private String jobName;
private boolean jobRunning;
private Trigger trigger;
private JobDetail detail;
public RunningJobListener(String jobName) {
this.jobName = jobName;
}
public String getName() {
return jobName;
}
public void jobExecutionVetoed(JobExecutionContext ctx) {
if ( ctx.getJobDetail().getName().equals( jobName ) ) {
jobRunning = false;
}
}
public void jobToBeExecuted(JobExecutionContext ctx) {
if ( ctx.getJobDetail().getName().equals( jobName ) ) {
jobRunning = true;
}
}
public void jobWasExecuted(JobExecutionContext ctx, JobExecutionException execException) {
if ( ctx.getJobDetail().getName().equals( jobName ) ) {
jobRunning = false;
if ( trigger != null && detail != null ) {
trigger.setStartTime(new Date());
try {
ctx.getScheduler().scheduleJob(detail, trigger);
} catch (SchedulerException e) {
LOG.error("Error occured when rescheduling job " + jobName, e);
}
detail = null;
trigger = null;
}
}
}
public boolean isJobRunning() {
return jobRunning;
}
public void scheduleJob(JobDetail detail, Trigger trigger) {
this.trigger = trigger;
this.detail = detail;
}
}
public static void main( String[] args ) throws Exception {
//Batch core = new Batch( "jdbc:derby://localhost:1527/sonar", "org.apache.derby.jdbc.ClientDriver", "sonar", "sonar" );
Batch batch = new Batch( "jdbc:mysql://localhost:3306/sonar_development", "com.mysql.jdbc.Driver", "sonar", "sonar" );
batch.start();
batch.triggerJob(METRICS_CALCULATIONS_JOB_NAME);
}
}