/** * (C) Copyright 2013 Jabylon (http://www.jabylon.org) and others. * * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html */ /** * */ package org.jabylon.scheduler.internal; import java.util.concurrent.TimeUnit; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Status; import org.jabylon.common.progress.Progression; import org.jabylon.scheduler.JobExecution; import org.jabylon.scheduler.JobInstance; import org.quartz.InterruptableJob; import org.quartz.JobDataMap; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.quartz.UnableToInterruptJobException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * @author Johannes Utzig (jutzig.dev@googlemail.com) * */ public class JabylonJob implements InterruptableJob, JobInstance { private static final int MAX_RETRIES = 5; private static final String KEY_RETRY_COUNT = "count"; private Thread thread; private ProgressionImpl monitor; private JobExecutionContext context; public static final String CONNECTOR_KEY = "repository.connector"; /** the execution service instance */ public static final String EXECUTION_KEY = "execution"; /** the key to retrieve the domain object for the current execution */ public static final String DOMAIN_OBJECT_KEY = "domain"; /** the key to retrieve the domain object for the current execution */ public static final String PROGRESS_MONITOR = "monitor"; private static final Logger LOG = LoggerFactory.getLogger(JabylonJob.class); /* (non-Javadoc) * @see org.quartz.Job#execute(org.quartz.JobExecutionContext) */ @Override public void execute(JobExecutionContext context) throws JobExecutionException { this.context = context; this.thread = Thread.currentThread(); long time = System.currentTimeMillis(); JobDataMap dataMap = context.getMergedJobDataMap(); JobExecution runnable = getExecutionObject(); if(!dataMap.containsKey(KEY_RETRY_COUNT)) dataMap.put(KEY_RETRY_COUNT, 0); int count = dataMap.getIntValue(KEY_RETRY_COUNT); // allow 5 retries if(count >= MAX_RETRIES){ JobExecutionException e = new JobExecutionException("Retries exceeded"); //make sure it doesn't run again LOG.error("Job "+runnable+" failed 5 times in a row. Deactivating"); e.setUnscheduleAllTriggers(true); throw e; } if(runnable==null) throw new IllegalStateException("No Job Execution was found"); try { monitor = new ProgressionImpl(); context.setResult(monitor); runnable.run(monitor, context.getMergedJobDataMap().getWrappedMap()); LOG.info("Job {} finished in {}ms",runnable,System.currentTimeMillis()-time); dataMap.put(KEY_RETRY_COUNT, 0); } catch (OperationCanceledException e) { monitor.setStatus(new Status(IStatus.CANCEL, "org.jabylon.common", null,e)); } catch (Exception e) { dataMap.put(KEY_RETRY_COUNT, count); if(runnable.retryOnError()) { try { count++; LOG.warn("Job {} failed. Retrying after 1 minute"); Thread.sleep(TimeUnit.MINUTES.toMillis(1l)); } catch (InterruptedException e1) { //nothing to do } } monitor.setStatus(new Status(IStatus.ERROR, "org.jabylon.common", null,e)); throw new JobExecutionException(e,runnable.retryOnError()); } finally{ this.thread = null; if(monitor.isCanceled() && monitor.getStatus()!=null) { //lower the severity if it was canceled IStatus status = monitor.getStatus(); IStatus newStatus = new Status(IStatus.CANCEL, status.getPlugin(), status.getMessage()); monitor.setStatus(newStatus); } log(monitor.getStatus()); if(!runnable.retryOnError() || count >= MAX_RETRIES) monitor.done(); } } private void log(IStatus status) { int severity = status.getSeverity(); switch (severity) { case IStatus.OK: LOG.debug("finished job {} : {}", getID(), status.getMessage()); break; case IStatus.CANCEL: LOG.debug("canceled job "+getID() + " : "+ status.getMessage(),status.getException()); break; case IStatus.WARNING: LOG.warn("job "+getID() + " finished with warning: "+ status.getMessage(),status.getException()); break; case IStatus.ERROR: LOG.error("job "+getID() + " failed: "+ status.getMessage(),status.getException()); break; default: break; } } /* (non-Javadoc) * @see org.quartz.InterruptableJob#interrupt() */ @Override public void interrupt() throws UnableToInterruptJobException { if(monitor!=null) monitor.setCanceled(true); //this seems to choke up CDO pretty badly, so it's deactivated for now // if(thread!=null) // thread.interrupt(); } @Override public String getID() { return context.getJobDetail().getKey().getName(); } @Override public Progression getProgress() { return monitor; } @SuppressWarnings("unchecked") @Override public <T> T getDomainObject() { JobDataMap dataMap = context.getMergedJobDataMap(); return (T) dataMap.get(DOMAIN_OBJECT_KEY); } @Override public String getDescription() { return context.getJobDetail().getDescription(); } @Override public JobExecution getExecutionObject() { return (JobExecution) context.getMergedJobDataMap().get(EXECUTION_KEY); } }