/** * The contents of this file are subject to the OpenMRS Public License * Version 1.0 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License at * http://license.openmrs.org * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the * License for the specific language governing rights and limitations * under the License. * * Copyright (C) OpenMRS, LLC. All Rights Reserved. */ package org.openmrs.scheduler; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Calendar; import java.util.Date; import java.util.Map; import java.util.Properties; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openmrs.api.context.Context; import org.openmrs.util.PrivilegeConstants; public class SchedulerUtil { private static Log log = LogFactory.getLog(SchedulerUtil.class); /** * Start the scheduler given the following start up properties. * * @param p properties used to start the service */ public static void startup(Properties p) { // Override the Scheduler constants if specified by the user String val = p.getProperty("scheduler.username", null); if (val != null) { SchedulerConstants.SCHEDULER_DEFAULT_USERNAME = val; log.warn("Deprecated runtime property: scheduler.username. Value set in global_property in database now."); } val = p.getProperty("scheduler.password", null); if (val != null) { SchedulerConstants.SCHEDULER_DEFAULT_PASSWORD = val; log.warn("Deprecated runtime property: scheduler.username. Value set in global_property in database now."); } // TODO: do this for all services try { Context.addProxyPrivilege(PrivilegeConstants.MANAGE_SCHEDULER); Context.getSchedulerService().onStartup(); } finally { Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_SCHEDULER); } } /** * Shutdown the scheduler service that is statically associated with the Context class. */ public static void shutdown() { SchedulerService service = null; // ignores errors while getting the scheduler service try { service = Context.getSchedulerService(); } catch (Throwable t) { // pass } // TODO: Do this for all services try { Context.addProxyPrivilege(PrivilegeConstants.MANAGE_SCHEDULER); // doesn't attempt shutdown if there was an error getting the scheduler service if (service != null) { service.onShutdown(); } } finally { Context.removeProxyPrivilege(PrivilegeConstants.MANAGE_SCHEDULER); } } /** * Sends an email with system information and the given exception * * @param error */ public static void sendSchedulerError(Throwable throwable) { try { Context.openSession(); Boolean emailIsEnabled = Boolean.valueOf(Context.getAdministrationService().getGlobalProperty( SchedulerConstants.SCHEDULER_ADMIN_EMAIL_ENABLED_PROPERTY)); if (emailIsEnabled) { // Email addresses seperated by commas String recipients = Context.getAdministrationService().getGlobalProperty( SchedulerConstants.SCHEDULER_ADMIN_EMAIL_PROPERTY); // Send message if if (recipients != null) { // TODO need to use the default sender for the application String sender = SchedulerConstants.SCHEDULER_DEFAULT_FROM; String subject = SchedulerConstants.SCHEDULER_DEFAULT_SUBJECT + " : " + throwable.getClass().getName(); String message = new String(); message += "\n\nStacktrace\n============================================\n"; message += SchedulerUtil.getExceptionAsString(throwable); message += "\n\nSystem Variables\n============================================\n"; for (Map.Entry<String, String> entry : Context.getAdministrationService().getSystemVariables() .entrySet()) { message += entry.getKey() + " = " + entry.getValue() + "\n"; } // TODO need to the send the IP information for the server instance that is running this task log.debug("Sending scheduler error email to [" + recipients + "] from [" + sender + "] with subject [" + subject + "]:\n" + message); Context.getMessageService().sendMessage(recipients, sender, subject, message); } } } catch (Exception e) { // Log, but otherwise suppress errors log.warn("Could not send scheduler error email: ", e); } finally { Context.closeSession(); } } /** * @param e * @return <code>String</code> representation of the given exception */ public static String getExceptionAsString(Throwable t) { StringWriter stringWriter = new StringWriter(); PrintWriter printWriter = new PrintWriter(stringWriter, true); t.printStackTrace(printWriter); printWriter.flush(); stringWriter.flush(); return stringWriter.toString(); } /** * Gets the next execution time based on the initial start time (possibly years ago, depending * on when the task was configured in OpenMRS) and the repeat interval of execution. We need to * calculate the "next execution time" because the scheduled time is most likely in the past and * the JDK timer will run the task X number of times from the start time until now in order to * catch up. The assumption is that this is not the desired behavior -- we just want to execute * the task on its next execution time. For instance, say we had a scheduled task that ran every * 24 hours at midnight. In the database, the task would likely have a past start date (i.e. * 04/01/2006 12:00am). If we scheduled the task using the JDK Timer * scheduleAtFixedRate(TimerTask task, Date startDate, int interval) method and passed in the * start date above, the JDK Timer would execute this task once for every day between the start * date and today, which would lead to hundreds of unnecessary (and likely expensive) * executions. * * @see java.util.Timer * @param taskDefinition the task definition to be executed * @return the next "future" execution time for the given task * @should get the correct repeat interval */ public static Date getNextExecution(TaskDefinition taskDefinition) { Calendar nextTime = Calendar.getInstance(); try { Date firstTime = taskDefinition.getStartTime(); if (firstTime != null) { // Right now Date currentTime = new Date(); // If the first time is actually in the future, then we use that date/time if (firstTime.after(currentTime)) { return firstTime; } // The time between successive runs (i.e. 24 hours) long repeatInterval = taskDefinition.getRepeatInterval().longValue(); // Calculate time between the first time the process was run and right now (i.e. 3 days, 15 hours) long betweenTime = currentTime.getTime() - firstTime.getTime(); // Calculate the last time the task was run (i.e. 15 hours ago) long lastTime = (betweenTime % (repeatInterval * 1000)); // Calculate the time to add to the current time (i.e. 24 hours - 15 hours = 9 hours) long additional = ((repeatInterval * 1000) - lastTime); nextTime.setTime(new Date(currentTime.getTime() + additional)); log.debug("The task " + taskDefinition.getName() + " will start at " + nextTime.getTime()); } } catch (Exception e) { log.error("Failed to get next execution time for " + taskDefinition.getName()); } return nextTime.getTime(); } }