package nl.ipo.cds.executor; import nl.idgis.commons.jobexecutor.JobExecutor; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.*; import javax.sql.DataSource; import java.sql.Connection; import java.sql.SQLException; /** * Explicitly do not catch Exceptions. The Process-es itself are responsible for catching Exceptions * and decide if an exception is handable cq recoverable. If not the Process-es propagate RuntimeExceptions * which cause the JobExecutor to stop. * * @author eshuism * */ public class CdsJobExecuter { private static final int ADVISORY_LOCK_KEY = 0; private static final Log technicalLog = LogFactory.getLog(CdsJobExecuter.class); private final DataSource dataSource; private final JobExecutor jobExecutor; public CdsJobExecuter (final DataSource dataSource, final JobExecutor jobExecutor) { this.dataSource = dataSource; this.jobExecutor = jobExecutor; } public DataSource getDataSource () { return dataSource; } public JobExecutor getJobExecutor () { return jobExecutor; } @Configuration @ComponentScan (basePackageClasses = { nl.ipo.cds.executor.config.Package.class, nl.ipo.cds.metadata.config.Package.class }) @ImportResource ({ "/nl/ipo/cds/dao/dataSource-applicationContext.xml", "/nl/ipo/cds/dao/dao-applicationContext.xml" }) public static class Config { @Bean public ConfigDir configDir (final @Value("file:${CONFIGDIR}") String configDirPath) { return new ConfigDir (configDirPath); } } public static void main(String[] args) { try { AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext (Config.class); final CdsJobExecuter jobExecuter = applicationContext.getBean (CdsJobExecuter.class); jobExecuter.run(); applicationContext.close (); } catch(Exception e) { technicalLog.error("Uncaught exception", e); System.exit(1); } System.exit(0); } void run() throws InterruptedException, SQLException { technicalLog.info("CdsJobExecuter started"); Connection lockConnection = acquireAdvisoryLock(); if(lockConnection == null) { technicalLog.info("Couldn't acquire lock (JobExecutor already running?)"); return; } jobExecutor.run (); lockConnection.close(); technicalLog.info("CdsJobExecuter terminated"); } /** * Acquire advisory lock to ensure that only a single instance of * the JobExecutor is operating on the database. * * The database connection is obtained directly from the connection pool * (= not using Spring's DataSourceUtils) in order to be able to switch to * auto commit mode. * * As this connection is maintained for the entire lifetime of the JobExecutor, * it is imperative that no transaction is started! * * @return * @throws SQLException */ private Connection acquireAdvisoryLock() throws SQLException { Connection connection = dataSource.getConnection(); connection.setAutoCommit(true); /* Statement appNameStmt = connection.createStatement(); appNameStmt.execute("set application_name = 'JobExecutor (lock)'"); appNameStmt.close(); PreparedStatement lockStmt = connection.prepareStatement("select pg_try_advisory_lock(?)"); lockStmt.setLong(1, ADVISORY_LOCK_KEY); ResultSet rs = lockStmt.executeQuery(); boolean result = false; while(rs.next()) { result = rs.getBoolean(1); } rs.close(); lockStmt.close(); if(!result) { connection.close(); connection = null; } */ return connection; } }