package au.com.vaadinutils.jasper.scheduler;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.joda.time.DateTime;
import au.com.vaadinutils.crud.CrudEntity;
import au.com.vaadinutils.crud.events.CrudEventDistributer;
import au.com.vaadinutils.crud.events.CrudEventListener;
import au.com.vaadinutils.crud.events.CrudEventType;
import au.com.vaadinutils.dao.JpaBaseDao;
import au.com.vaadinutils.jasper.JasperEmailSettings;
import au.com.vaadinutils.jasper.scheduler.entities.ScheduleMode;
public class Scheduler implements Runnable
{
public static final String REPORT_SUCCESSFULLY_RUN = "Report successfully run";
Logger logger = LogManager.getLogger();
final private ReportEmailScheduleProvider scheduleProvider;
final private ScheduledExecutorService schedulerpool = Executors.newScheduledThreadPool(1);
final private ReportEmailRunner reportRunner;
final JasperEmailSettings emailSettings;
final private ScheduledFuture<?> future;
private DBmanager dbManager;
// the soonest time any report needs to be run
AtomicReference<DateTime> next = new AtomicReference<DateTime>();
Scheduler(ReportEmailScheduleProvider scheduleProvider, ReportEmailRunner reportRunner,
JasperEmailSettings emailSettings, DBmanager dbManager)
{
next.set(new DateTime());
this.scheduleProvider = scheduleProvider;
this.reportRunner = reportRunner;
this.emailSettings = emailSettings;
this.dbManager = dbManager;
future = schedulerpool.scheduleAtFixedRate(this, 1, 1, TimeUnit.MINUTES);
CrudEventDistributer.addListener(JasperReportScheduleLayout.class, new CrudEventListener()
{
@Override
public void crudEvent(CrudEventType event, CrudEntity entity)
{
next.set(new DateTime());
}
});
}
public void reschedule()
{
next.set(new DateTime());
}
@Override
public synchronized void run()
{
try
{
if (next.get().isBeforeNow())
{
protectedRun();
}
}
catch (Exception e)
{
logger.error(e, e);
}
}
private void protectedRun()
{
DateTime currentNext = next.get();
DateTime nextPossible = new DateTime().plusDays(1).withTimeAtStartOfDay();
Thread.currentThread().setName("Jasper Report Scheduler");
List<ReportEmailSchedule> schedules = Collections.emptyList();
try
{
dbManager.beginDbTransaction();
schedules = scheduleProvider.getSchedules();
}
finally
{
dbManager.commitDbTransaction();
}
for (ReportEmailSchedule schedule : schedules)
{
if (schedule.isEnabled())
{
try
{
DateTime now = new DateTime();
dbManager.beginDbTransaction();
schedule = JpaBaseDao.getEntityManager().merge(schedule);
Date nextScheduledTime = schedule.getNextScheduledTime();
if (nextScheduledTime == null)
{
// upgrate existing schedules that dont have a next
// runtime
nextScheduledTime = schedule.getScheduleMode().getNextRuntime(schedule, now.toDate());
schedule.setNextScheduledRunTime(nextScheduledTime);
}
Date lastRuntime = schedule.getLastRuntime();
if ((lastRuntime == null || lastRuntime.before(nextScheduledTime))
&& nextScheduledTime.before(now.toDate()))
{
if (reportRunner.runReport(schedule, nextScheduledTime, emailSettings))
{
schedule.setLastRuntime(now.toDate(), REPORT_SUCCESSFULLY_RUN);
if (schedule.getScheduleMode() == ScheduleMode.ONE_TIME)
{
scheduleProvider.delete(schedule);
}
else
{
schedule.setNextScheduledRunTime(
schedule.getScheduleMode().getNextRuntime(schedule, now.toDate()));
}
}
else
{
logger.warn("Report queue is not empty, will try scheduled report again later " + schedule);
}
}
}
catch (Exception e)
{
schedule.setEnabled(false);
String message = e.getMessage();
if (StringUtils.isNotEmpty(message))
{
message = message.substring(0, Math.min(1000, message.length() - 1));
}
schedule.setLastRuntime(new Date(), message);
logger.error(e, e);
}
finally
{
dbManager.commitDbTransaction();
}
if (nextPossible.isAfter(schedule.getNextScheduledTime().getTime()))
{
nextPossible = new DateTime(schedule.getNextScheduledTime().getTime());
}
}
}
if (!next.compareAndSet(currentNext, nextPossible))
{
// next was updated while we were running, so run again as soon
// as possible
next.set(new DateTime());
}
}
public void stop()
{
schedulerpool.shutdown();
future.cancel(true);
}
}