You can use the table in your database to store jobLock, but you should check / update this lock in separate transactions (for this you need to use JPA.newEntityManager)
My JobLock class uses the LockMode enumeration
package enums; public enum LockMode { FREE, ACQUIRED; }
here is the class JobLock
package models; import java.util.Date; import java.util.List; import javax.persistence.Entity; import javax.persistence.EntityManager; import javax.persistence.EnumType; import javax.persistence.Enumerated; import javax.persistence.Version; import play.Logger; import play.Play; import play.data.validation.Required; import play.db.jpa.JPA; import play.db.jpa.Model; import utils.Parser; import enums.LockMode; import exceptions.ServiceException; @Entity public class JobLock extends Model { private static final Long MAX_ACQUISITION_DELAY = Parser.parseLong(Play.configuration.getProperty( "job.lock.acquisitiondelay", "10000")); @Required public String jobName; public Date acquisitionDate; @Required @Enumerated(EnumType.STRING) public LockMode lockMode; @Version public int version;
and here is my LockAwareJob that uses this lock
package jobs; import models.JobLock; import notifiers.ExceptionMails; import play.Logger; import play.jobs.Job; public abstract class LockAwareJob<V> extends Job<V> { @Override public final void doJob() throws Exception { String name = this.getClass().getSimpleName(); try { JobLock lock = JobLock.acquireLock(name); if (lock != null) { Logger.info("Starting %s", name); try { doJobWithLock(lock); } finally { if (!JobLock.releaseLock(lock)) { Logger.error("Lock acquired but cannot be released for %s", name); } Logger.info("End of %s", name); } } else { Logger.info("Another node is running %s : nothing to do", name); } } catch (Exception ex) { ExceptionMails.exception(ex, String.format("Error while executing job %s", name)); throw ex; } } @Override public final V doJobWithResult() throws Exception { String name = this.getClass().getSimpleName(); try { JobLock lock = JobLock.acquireLock(name); if (lock != null) { Logger.info("Starting %s", name); try { return resultWithLock(lock); } finally { if (!JobLock.releaseLock(lock)) { Logger.error("Lock acquired but cannot be released for %s", name); } Logger.info("End of %s", name); } } else { Logger.info("Another node is running %s : nothing to do", name); return resultWithoutLock(); } } catch (Exception ex) { ExceptionMails.exception(ex, String.format("Error while executing job %s", name)); throw ex; } } public void doJobWithLock(JobLock lock) throws Exception { } public V resultWithLock(JobLock lock) throws Exception { doJobWithLock(lock); return null; } public V resultWithoutLock() throws Exception { return null; } }
In my log4j.properties I add a special line to avoid an error every time the instance was unable to get the job lock
log4j.logger.org.hibernate.event.def.AbstractFlushingEventListener=FATAL
With this solution, you can also use the JobLock ID to store the parameters associated with this job (e.g. last run)
Seb cesbron
source share