package jeffaschenk.commons.system.internal.scheduling;
import java.util.*;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import fr.dyade.jdring.AlarmEntry;
import fr.dyade.jdring.AlarmListener;
import fr.dyade.jdring.AlarmManager;
import fr.dyade.jdring.PastDateException;
import jeffaschenk.commons.system.internal.file.services.ServiceTask;
import jeffaschenk.commons.system.internal.scheduling.events.LifeCycleServiceStateType;
import jeffaschenk.commons.system.internal.scheduling.events.LifeCycleServiceType;
import jeffaschenk.commons.system.internal.scheduling.events.LifeCycleServicesEvent;
import jeffaschenk.commons.touchpoint.model.dao.SystemDAO;
import jeffaschenk.commons.environment.SystemEnvironmentBean;
import jeffaschenk.commons.touchpoint.model.transitory.ParsedAlarmEntry;
import jeffaschenk.commons.touchpoint.model.wrappers.CollectionMapBean;
import jeffaschenk.commons.types.EnvironmentType;
import jeffaschenk.commons.util.StringUtils;
import jeffaschenk.commons.util.TimeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.scheduling.TaskScheduler;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
/**
* Content Management Service Interface Provides Access to resolve any
* dynamic content directly from an established cache or
*
* @author jeffaschenk@gmail.com
*/
@Service("localSchedulingService")
public class LocalSchedulingServiceImpl implements LocalSchedulingService,
ApplicationContextAware {
/**
* Logging
*/
private final static Logger logger = LoggerFactory
.getLogger(LocalSchedulingServiceImpl.class);
/**
* Globals
*/
private final static String CRON_ELEMENT_SEPARATORS = "-,/";
private final static String AUCTION = "AUCTION";
/**
* Initialization Indicator.
*/
private boolean initialized = false;
/**
* Injected Common System Environment Property to check to see if this
* facility is enabled or not.
*/
@Value("#{systemEnvironmentProperties['internal.scheduling.tasks.enabled']}")
private boolean serviceEnabled;
/**
* Injected Common System Environment Property to configure the CRON. prefix
* for specified system Environment Variables.
*/
@Value("#{systemEnvironmentProperties['internal.scheduling.tasks.cron.prefix']}")
private String CRON_SEARCH_KEY;
/**
* Global Services Injected
*/
@Autowired
protected SystemDAO systemDAO;
@Resource(name = "cronKeyToBeanMap")
private CollectionMapBean cronKeyToBeanMap;
@Autowired
@Qualifier("systemEnvironmentPropertyAccessor")
private SystemEnvironmentBean systemEnvironmentBean;
/**
* Task Scheduler
*/
@Autowired
private TaskScheduler taskScheduler;
/**
* Task Executor
*/
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
/**
* JDRING Global Scheduler
*/
private AlarmManager alarmManager;
/**
* Major Running Service Task States.
*/
private Map<LifeCycleServiceType, LifeCycleServiceTaskState> taskStates = new HashMap<>();
/**
* Spring Application Context, used to obtain access to Resources on
* Classpath.
*/
private ApplicationContext applicationContext;
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* Initialize the Content Management System Interface
*/
@PostConstruct
public void initialize() {
// ************************************************************
// Ensure we have our System DAO Available.
if (this.systemDAO == null) {
logger
.error("Application Context Initialization is suspect, as one or more required Services are not available at this time, Disabling Service.");
this.initialized = false;
return;
}
// *********************************************************************
// Ensure Service Enabled and The Runtime Environment is a JVM.
// We will not initialize our Internal Scheduler if our Environment Type
// is not "JVM", however, for "ESB", we will auto-initialize and
// filter what types of Alarm/Events will be started.
//
if ((this.serviceEnabled)
&& (this.systemEnvironmentBean.runningEnvironmentType() == EnvironmentType.JVM)) {
logger
.warn("Activating Local Scheduling Service Provider Interface.");
// *************************************************************
// Construct our Alarm Manager.
this.alarmManager = new AlarmManager(true,
"SOR_INTERNAL_AlarmManager");
// *************************************************************
// Show our Cron Class Key to ClassName Map.
if ((cronKeyToBeanMap == null)
|| (cronKeyToBeanMap.getMap().isEmpty())) {
logger
.error("Unable to Access Cron Key to Bean Name, Disabling Service.");
this.initialized = false;
return;
}
for (String key : cronKeyToBeanMap.getMap().keySet()) {
logger.warn(" * CRON Key:[" + key
+ "], References Bean Name:["
+ cronKeyToBeanMap.getMap().get(key) + "]");
}
// *************************************************************
// Now use System Services to Obtain the SysEnvironment
// CRON Keys, spin through and activate all Triggers
this.initializeSchedulerFromPersistentStore(CRON_SEARCH_KEY, null);
// *************************************************************
// Show Summary of Schedule
List list = this.alarmManager.getAllAlarms();
for (Iterator it = list.iterator(); it.hasNext();) {
logger.warn("Scheduled Alarm:[" + it.next() + "]");
}
// initialized
this.initialized = true;
} else if (this.systemEnvironmentBean.runningEnvironmentType() == EnvironmentType.ESB) {
logger
.warn("Activating Local Scheduling Service Provider Interface with Filtered Alarm Definitions for Environment:["
+ this.systemEnvironmentBean
.runningEnvironmentType().name() + "]");
// *************************************************************
// Construct our Alarm Manager.
this.alarmManager = new AlarmManager(true,
"SOR_INTERNAL_ESB_AlarmManager");
// *************************************************************
// Show our Cron Class Key to ClassName Map.
if ((cronKeyToBeanMap == null)
|| (cronKeyToBeanMap.getMap().isEmpty())) {
logger
.error("Unable to Access Cron Key to Bean Name, Disabling Service.");
this.initialized = false;
return;
}
for (String key : cronKeyToBeanMap.getMap().keySet()) {
if (key.toUpperCase().contains(AUCTION)) {
continue;
}
logger.warn(" * CRON Key:[" + key
+ "], References Bean Name:["
+ cronKeyToBeanMap.getMap().get(key) + "]");
}
// *************************************************************
// Now use System Services to Obtain the SysEnvironment
// CRON Keys, spin through and activate all Triggers
this.initializeSchedulerFromPersistentStore(CRON_SEARCH_KEY,
AUCTION);
// *************************************************************
// Show Summary of Schedule
List list = this.alarmManager.getAllAlarms();
for (Iterator it = list.iterator(); it.hasNext();) {
logger.warn("Scheduled Alarm:[" + it.next() + "]");
}
// initialized
this.initialized = true;
} else {
logger
.warn("Local Scheduling Provider Interface has been Disabled.");
this.initialized = false;
}
}
/**
* Destroy Service Invoked during Termination of the Spring Container.
*/
@PreDestroy
public void destroy() {
if (this.initialized) {
logger
.warn("Deactivating Local Scheduling Service Provider Interface");
if (this.alarmManager != null) {
this.alarmManager.removeAllAlarms();
this.alarmManager.finalize();
}
// ***************************
// Indicate not initialized!
initialized = false;
logger
.warn("Deactivation of Local Scheduling Provider Interface was Successful.");
}
}
/**
* Schedule any Necessary Actions Obtained Directly from SysEnvironment
*
* @param actionBeanName
* of Internally Scheduler Action.
* @param cronExpression
* @return boolean
*/
@Override
public boolean scheduleAction(String actionBeanName, String cronExpression) {
// ******************************************
// Can we schedule an Action?
if (!this.serviceEnabled) {
return false;
}
// ******************************************
// Check against Internal Gate
// TODO Track Job Initialization to inhibit lots of activity.
// *******************************************
// Validate Parameters.
if (StringUtils.isEmpty(actionBeanName)) {
return false;
}
if (StringUtils.isEmpty(cronExpression)) {
return false;
}
// ********************************************
// Initialize a Alarm Entry for this Action
try {
AlarmEntry alarmEntry = this.parseCronExpression(actionBeanName,
cronExpression, this
.obtainComponentReference(actionBeanName));
if (alarmEntry != null) {
this.alarmManager.addAlarm(alarmEntry);
return true;
}
} catch (PastDateException pde) {
logger.error(
"Unable to Instantiate Scheduled Action Class Exception: "
+ pde.getMessage(), pde);
}
return false;
}
/**
* Provides ability to remove an Action based upon a
* the name of the Action.
*
* @param actionBeanName
* @return boolean indicating if Action has een removed.
*/
@SuppressWarnings("unchecked")
public boolean removeAction(String actionBeanName) {
// *******************************************
// Validate Parameters.
if (StringUtils.isEmpty(actionBeanName)) {
return false;
}
// ************************************************
// Find our Existing a Alarm Entry for this Action
List<AlarmEntry> entries = this.alarmManager.getAllAlarms();
if ((entries == null) || (entries.isEmpty())) {
return false;
}
// Spin through and Find our Entry.
AlarmEntry entryToBeRemoved = null;
for (AlarmEntry entry : entries) {
// TODO FIX --
//if (entry.getName().equalsIgnoreCase(actionBeanName)) {
// entryToBeRemoved = entry;
//}
}
if (entryToBeRemoved == null) {
return false;
}
// ***********************************************
// Remove Existing a Alarm Entry for this Action
logger.info("Attempting removal of Alarm Entry:[" + entryToBeRemoved.toString() + "]");
return this.alarmManager.removeAlarm(entryToBeRemoved);
}
/**
* Private helper method to Obtain Component Reference of bean Class
*
* @param actionBeanName
* of Object to return
* @return Object of Instantiated Class or Null.
*/
private AlarmListener obtainComponentReference(String actionBeanName) {
AlarmListener alarmListener = (AlarmListener) applicationContext
.getBean(actionBeanName);
if (alarmListener == null) {
logger
.error("Unable to obtain Component Action Alarm Listener Reference!");
}
// *************************************
// Return Instantiated Object or Null.
return alarmListener;
}
/**
* Private Helper Method to establish Stored CRON Entries to our Scheduler.
*
* @param key
*/
private void initializeSchedulerFromPersistentStore(String key,
String excludeFilter) {
// *************************************************************
// Now use System Services to Obtain the SysEnvironment
// CRON Keys, spin through and activate all Triggers
Map<String, String> cronEntriesFromDB = this.systemDAO
.findSysEnvironmentProperty(key + "%", null);
if ((cronEntriesFromDB.size() > 0)) {
for (String cronKey : cronEntriesFromDB.keySet()) {
String cronValue = cronEntriesFromDB.get(cronKey);
if (StringUtils.isEmpty(cronValue)) {
continue;
}
// Filter and Exclude anything containing a piece of the named
// filter.
if ((excludeFilter != null)
&& (cronKey.toUpperCase().contains(excludeFilter))) {
continue;
}
// Establish the Cron Task in our Container Scheduler.
String actionBeanName = resolveCronClassKey(cronKey
.substring(key.length()));
// Now Initialize the Job and Introduce into our Schedule.
this.scheduleAction(actionBeanName, cronValue);
}
} else {
logger
.warn("No CRONTAB Entries found in this Instance Store for use, using Cron Tab Key:["
+ key + "]");
}
}
/**
* Simple Private Helper Method to resolve the specific class per the Class
* Key Name.
*
* @param cronClassKey
* @return String
*/
@Override
public String resolveCronClassKey(String cronClassKey) {
if (cronKeyToBeanMap.getMap().get(cronClassKey.trim().toUpperCase()) == null) {
logger.warn("No Associated Class found with CRON Key:["
+ cronClassKey + "], check your configuration!");
return null;
}
return cronKeyToBeanMap.getMap().get(cronClassKey.toUpperCase()).toString();
}
/**
* Parse the cronExpress and produce an AlarmEntry for the Alarm Manager to
* use for scheduling.
* <p/>
* CronExpression Examples:
* <p/>
* Cron Example Patterns:
* <p/>
* S m h d M DOW 0 0 * * * * = the top of every hour of every day. 0/10 * *
* * * * = every ten seconds. ** Will be Ignored! 0 0 8-10 * * * = 8, 9 and
* 10 o'clock of every day. 0 0/30 8-10 * * * = 8:00, 8:30, 9:00, 9:30 and
* 10 o'clock every day. 0 0 9-17 * * MON-FRI = on the hour nine-to-five
* weekdays 0 0 0 25 12 ? = every Christmas Day at midnight
* <p/>
* Now what is actually down to parse is to ignore the seconds completely,
* if seconds are specified, we will ignore for now at least. But the intent
* of the internal scheduler is to Alarm at certain times and perform a
* task.
* <p/>
*** Any task that is needed to every every x number of seconds, needs to
* run in its own thread not here.
*
* @param cronExpression
* @param alarmListener
* - Instantiated Alarm Listener for the produced Alarm Entry.
* @return AlarmEntry
*/
private AlarmEntry parseCronExpression(String name, String cronExpression,
AlarmListener alarmListener) {
// ***************************************
// Initialize
AlarmEntry alarmEntry;
int _year = -1;
// ***************************************
// Validate
if ((StringUtils.isEmpty(name))
|| (StringUtils.isEmpty(cronExpression))
|| (alarmListener == null)) {
logger
.error("Unable to Instantiate new Alarm Entry, one or more method parameters invalid!");
return null;
}
// ***************************************
// Parse String
String[] cronArray = cronExpression.split(" ");
if (cronArray.length < 6) {
logger.error("Cron Expression Invalid:[" + cronExpression
+ "], needs at least 6 parameters!");
return null;
}
logger.warn("Parsed Cron Expression from Persistent Store:["
+ "seconds:[" + cronArray[0] + "], " + "minutes:["
+ cronArray[1] + "], " + "hours:[" + cronArray[2] + "], "
+ "dayOfMonth:[" + cronArray[3] + "], " + "month:["
+ cronArray[4] + "], " + "dayOfWeek:[" + cronArray[5] + "]]");
// ***************************************
// Formulate each Alarm Component.
int x = 0;
int[][] formulatedCronElementArrays = new int[6][];
for (String cronElement : cronArray) {
// Now Parse each element grouping.
List<Integer> ticList = new ArrayList<Integer>();
StringTokenizer st = new StringTokenizer(cronElement.trim(),
CRON_ELEMENT_SEPARATORS, true);
String lastTick = null;
String lastSeparator = null;
while (st.hasMoreTokens()) {
String token = st.nextToken();
if ((token == null) || (token.trim().length() <= 0)) {
continue;
}
if (StringUtils.isNumeric(token)) {
if (StringUtils.isEmpty(lastSeparator)) {
try {
ticList.add(new Integer(token));
} catch (NumberFormatException nfe) {
logger
.error("Invalid Cron Element should have been a numeric value, but was:["
+ token + "], Ignoring Alarm!");
return null;
}
} else if (lastSeparator.equalsIgnoreCase(",")) {
try {
ticList.add(new Integer(token));
} catch (NumberFormatException nfe) {
logger
.error("Invalid Cron Element should have been a numeric value, but was:["
+ token + "], Ignoring Alarm!");
return null;
}
} else if (lastSeparator.equalsIgnoreCase("-")) {
try {
int base = Integer.valueOf((StringUtils
.isEmpty(lastTick) ? "0" : lastTick));
int last = Integer.valueOf(token);
if (base > last) {
logger
.error("Invalid Cron Element using dashed sequence, base greater than last:["
+ token + "], Ignoring Alarm!");
return null;
} else if (base < last) {
for (int i = base + 1; i <= last; i++) {
ticList.add(new Integer(i));
}
} else {
ticList.add(new Integer(token));
}
} catch (NumberFormatException nfe) {
logger
.error("Invalid Cron Element should have been a numeric value, but was:["
+ token + "], Ignoring Alarm!");
return null;
}
} else if (lastSeparator.equalsIgnoreCase("/")) {
// Only use this notation for seconds / minutes or hours
try {
int base = Integer.valueOf((StringUtils
.isEmpty(lastTick) ? "0" : lastTick));
int increment = Integer.valueOf(token);
int end = 60;
if (x > 2) {
logger
.error("Invalid Cron Element unable to use '/' notation for specified parameter:["
+ token + "], Ignoring Alarm!");
return null;
}
if (x == 2) {
end = 23;
}
for (int i = base + increment; i < end; i = i
+ increment) {
ticList.add(new Integer(i));
}
} catch (NumberFormatException nfe) {
logger
.error("Invalid Cron Element should have been a numeric value, but was:["
+ token + "], Ignoring Alarm!");
return null;
}
}
lastSeparator = null;
lastTick = token;
} else if (CRON_ELEMENT_SEPARATORS.indexOf(token) > -1) {
if (StringUtils.isEmpty(lastSeparator)) {
lastSeparator = token; // Set up to trigger usage.
} else {
logger
.error("Invalid Cron Element back to back Separator Values found, Previous:["
+ lastSeparator
+ ", This:["
+ token
+ "], Ignoring Alarm!");
return null;
}
} else if ((token.equalsIgnoreCase("*"))
|| (token.equalsIgnoreCase("?"))) {
ticList.add(new Integer(-1));
} else {
logger
.error("Invalid Cron Element Unidentifiable Character/Phrase Found:["
+ token + "], Ignoring Alarm!");
return null;
}
}
// now place the produced Element Array into a the formulated Array
// to produce the Alarm Entry.
formulatedCronElementArrays[x] = new int[ticList.size()];
int i = -1;
for (Integer tic : ticList) {
i++;
formulatedCronElementArrays[x][i] = tic.intValue();
}
x++;
} // End of For Each Loop.
try {
/**
alarmEntry = new AlarmEntry(name, formulatedCronElementArrays[1],
formulatedCronElementArrays[2],
formulatedCronElementArrays[3],
formulatedCronElementArrays[4],
formulatedCronElementArrays[5], _year, alarmListener);
**/
alarmEntry = new AlarmEntry(formulatedCronElementArrays[1][0],
formulatedCronElementArrays[2][0],
formulatedCronElementArrays[3][0],
formulatedCronElementArrays[4][0],
formulatedCronElementArrays[5][0], _year, alarmListener);
logger.warn("Created Alarm Entry:[" + alarmEntry.toString() + "]");
// **********************************
// return New alarm Entry based upon
// Cron Expression and other
// default values.
return alarmEntry;
} catch (PastDateException pde) {
logger
.error("Can not initialize for a Date in the Past, Ignoring Cron Expression:["
+ cronExpression + "]");
return null;
}
}
/**
* Private Helper Method to parse the String version of
* the AlarmEntry to display nicely in HTML.
* <p/>
* What we will parse and prettify.
* <p/>
* Alarm (heartBeat) params minute={0, 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55}
* hour={-1} dayOfMonth={-1} month={-1} dayOfWeek={-1}
* (next alarm date=Sat Jan 28 17:55:00 PST 2012)
*
* @param entry
* @return ParsedAlarmEntry
*/
private ParsedAlarmEntry parseAlarmEntryString(AlarmEntry entry) {
if (entry == null) {
return null;
}
String str = entry.toString();
ParsedAlarmEntry parsedAlarmEntry = new ParsedAlarmEntry();
// TODO FIX -- parsedAlarmEntry.setName(entry.getName());
parsedAlarmEntry.setMinute(parseNamedElement(str, "minute={", "}"));
parsedAlarmEntry.setHour(parseNamedElement(str, "hour={", "}"));
parsedAlarmEntry.setDayOfMonth(parseNamedElement(str, "dayOfMonth={", "}"));
parsedAlarmEntry.setMonth(parseNamedElement(str, "month={", "}"));
parsedAlarmEntry.setDayOfWeek(parseNamedElement(str, "dayOfWeek={", "}"));
parsedAlarmEntry.setNextAlarm(parseNamedElement(str, "(next alarm date=", ")"));
// return Parsed Entry for Display
return parsedAlarmEntry;
}
/**
* Private Helper to Parse String of the AlarmEntry
*
* @param str
* @param beginChars
* @param endChar
* @return String
*/
private String parseNamedElement(String str, String beginChars, String endChar) {
String content = null;
int x = str.indexOf(beginChars);
if (x > 0) {
content = str.substring(x + beginChars.length());
int y = content.indexOf(endChar);
if (y > 0) {
content = content.substring(0, y);
}
if (content.equalsIgnoreCase("-1")) {
content = "Any";
}
// Transform to
// make the Entry Readable for Months, and Day of Week.
if (beginChars.startsWith("month")) {
content = this.transformMonth(content);
} else if (beginChars.startsWith("dayOfWeek")) {
content = this.transformDayOfWeek(content);
}
}
return content;
}
/**
* Private helper to transform Cron Month to Human Readable.
*
* @param rawData
* @return String
*/
private String transformMonth(final String rawData) {
StringBuffer sb = new StringBuffer();
for (String element : rawData.split(",")) {
if (sb.length() > 0) {
sb.append(",");
}
// Transform
switch (element.trim().toLowerCase()) {
case "0":
sb.append("Jan");
break;
case "1":
sb.append("Feb");
break;
case "2":
sb.append("Mar");
break;
case "3":
sb.append("Apr");
break;
case "4":
sb.append("May");
break;
case "5":
sb.append("Jun");
break;
case "6":
sb.append("Jul");
break;
case "7":
sb.append("Aug");
break;
case "8":
sb.append("Sep");
break;
case "9":
sb.append("Oct");
break;
case "10":
sb.append("Nov");
break;
case "11":
sb.append("Dec");
break;
default:
sb.append(element);
break;
}
}
return sb.toString();
}
/**
* Private helper to transform Cron Month to Human Readable.
*
* @param rawData
* @return String
*/
private String transformDayOfWeek(final String rawData) {
StringBuffer sb = new StringBuffer();
for (String element : rawData.split(",")) {
if (sb.length() > 0) {
sb.append(",");
}
// Transform
switch (element.trim().toLowerCase()) {
case "1":
sb.append("Sun");
break;
case "2":
sb.append("Mon");
break;
case "3":
sb.append("Tue");
break;
case "4":
sb.append("Wed");
break;
case "5":
sb.append("Thu");
break;
case "6":
sb.append("Fri");
break;
case "7":
sb.append("Sat");
break;
default:
sb.append(element);
break;
}
}
return sb.toString();
}
/**
* Method Entered When a LifeCycle Services Event gets published.
* This Method allows our scheduler to consume the Event and provide
* state information for all Tasks.
*
* @param event
*/
public void onApplicationEvent(LifeCycleServicesEvent event) {
logger.info("LifeCycle Services Event Consumed: " + event.toString());
// Check for Update Cleanup immediately...
if (event.getLifeCycleServiceStateType().equals(LifeCycleServiceStateType.UPDATE_CLEANUP)) {
logger.warn("Detected Update Clean-Up Event for:" + event.getMessage());
// **************************************************
// Process Completed Demographics LifeCycle,
// Remove the Update Payload elements,
// as those have now been processed.
//
if (event.getLifeCycleServiceType().index()
== LifeCycleServiceType.DEMOGRAPHIC_UPDATES_COMPLETED_LIFECYCLE.index()) {
// ***************************************************
//
// Data from Above Event:
//
// LifeCycle Services Event Consumed: LifeCycleServicesEvent{lifeCycleServiceType=DEMOGRAPHIC_UPDATES_COMPLETED_LIFECYCLE,
// lifeCycleServiceStateType=UPDATE_CLEANUP,
// timestamp_when_event_published=1326175084976,
// message='Demographics Alt_ID:[10152100] has a completed all existing Lifecycle Updates',
// payload=[alt_demographics_profile_update_id{alt_id=10152100, modification_timestamp=12272011 15:25:55},
// alt_demographics_profile_update_id{alt_id=10152100, modification_timestamp=12272011 15:26:49},
// alt_demographics_profile_update_id{alt_id=10152100, modification_timestamp=12272011 21:10:42}]}
// com.tmatrix.wga.services.scheduler.events.LifeCycleServicesEvent[
// source=com.tmatrix.wga.services.extract.ExtractLifecycleUpdateDeterminationForDemographics@34a012a6]
//
// Iterate Over Each Object Key and Remove From the database.
//for (Object update_id : event.getPayload()) {
// this.entityDAO.removeDemographicUpdate((alt_demographics_profile_update_id) update_id);
//}
} // End of Demographic Update LifeCycle
//
// Add Additional Update Closure Events here if needed...
//
}
// Do we have an Existing Task State?
if (this.taskStates.containsKey(event.getLifeCycleServiceType())) {
LifeCycleServiceTaskState lifeCycleServiceTaskState =
this.taskStates.get(event.getLifeCycleServiceType());
// Is the Task Done?
if (event.getLifeCycleServiceStateType().equals(LifeCycleServiceStateType.DONE)) {
lifeCycleServiceTaskState.setCurrentLifeCycleServiceState(LifeCycleServiceStateType.DONE);
lifeCycleServiceTaskState.setDone(TimeUtils.now());
} else if (event.getLifeCycleServiceStateType().equals(LifeCycleServiceStateType.FAILURE)) {
logger.warn("Detected Failure Event!");
lifeCycleServiceTaskState.setCurrentLifeCycleServiceState(LifeCycleServiceStateType.DONE);
lifeCycleServiceTaskState.setDone(TimeUtils.now());
}
}
return;
}
/**
* Schedule Runnable Wrapper Interface
*
* @param runnableTask
* @param scheduledTask
*/
public synchronized boolean scheduleTask(ServiceTask runnableTask, Date scheduledTask) {
// Determine if this task Type can Run at this time.
if (isTaskRunnable(runnableTask.getLifeCycleServiceType())) {
// Add Task to Task States.
LifeCycleServiceTaskState lifeCycleServiceTaskState =
new LifeCycleServiceTaskState(runnableTask.getLifeCycleServiceType());
lifeCycleServiceTaskState.setCurrentLifeCycleServiceState(LifeCycleServiceStateType.BEGIN);
lifeCycleServiceTaskState.setStarted(TimeUtils.now());
lifeCycleServiceTaskState.setDone(0);
taskStates.put(lifeCycleServiceTaskState.getLifeCycleServiceType(), lifeCycleServiceTaskState);
// Schedule Task
taskScheduler.schedule(runnableTask, scheduledTask);
logger.warn("Scheduled Task:[" + runnableTask.getLifeCycleServiceType().text() +
"] to be Executed at:[" + scheduledTask.toString() + "].");
return true;
}
return false;
}
/**
* Private Helper Method to determine if the Task is Runnable or not.
*
* @param lifeCycleServiceType
* @return boolean Indicator if Task can run or not.
*/
private boolean isTaskRunnable(LifeCycleServiceType lifeCycleServiceType) {
// Determine if we can run the requested Task at this time or not?
if (this.taskStates.containsKey(lifeCycleServiceType)) {
LifeCycleServiceTaskState lifeCycleServiceTaskState = this.taskStates.get(lifeCycleServiceType);
if ((!lifeCycleServiceTaskState.getCurrentLifeCycleServiceState().equals(LifeCycleServiceStateType.DONE)) ||
(lifeCycleServiceTaskState.getDone() <= 0)) {
logger.warn("Unable to Schedule Task:[" + lifeCycleServiceType.text() +
"], since task is currently running or has been scheduled.");
return false;
}
}
// Allow running Task Type.
return true;
}
/**
* Schedule Runnable Wrapper Interface
*
* @return String providing textual data representing active Thread count.
*/
@Override
public int getActiveThreadPoolTasks() {
return taskExecutor.getActiveCount();
}
/**
* Obtain our current schedule
*
* @return String - HTML Current Schedule Status.
*/
@SuppressWarnings("unchecked")
public String getCurrentSchedule() {
List<AlarmEntry> alarms = this.alarmManager.getAllAlarms();
StringBuffer sb = new StringBuffer();
if ((alarms == null) || (alarms.isEmpty())) {
sb.append("<br/><b><i>Currently there are no Processes scheduled.</i></b><br/>");
} else {
sb.append("<br/><b><i>Current Process Scheduled:</i></b><br/>");
sb.append("<table border='2'>");
sb.append("<tr>");
sb.append("<td>");
sb.append("<b>Alarm Name</b>");
sb.append("</td>");
sb.append("<td>");
sb.append("<b>Next Alarm</b>");
sb.append("</td>");
sb.append("</tr>");
for (AlarmEntry entry : alarms) {
ParsedAlarmEntry parsedAlarmEntry = this.parseAlarmEntryString(entry);
sb.append(parsedAlarmEntry.toHTML());
}
sb.append("</table>");
}
return sb.toString();
}
/**
* Obtain our current schedule
*
* @return String - HTML Current Schedule Status.
*/
@SuppressWarnings("unchecked")
public String getFullCurrentSchedule() {
List<AlarmEntry> alarms = this.alarmManager.getAllAlarms();
StringBuffer sb = new StringBuffer();
if ((alarms == null) || (alarms.isEmpty())) {
sb.append("<br/><b><i>Currently there are no Processes scheduled.</i></b><br/>");
} else {
sb.append("<br/><b><i>Current Process Scheduled:</i></b><br/>");
sb.append("<table border='2'>");
sb.append("<tr>");
sb.append("<td>");
sb.append("<b>Alarm Name</b>");
sb.append("</td>");
sb.append("<td>");
sb.append("<b>Next Alarm</b>");
sb.append("</td>");
sb.append("<td>");
sb.append("<b>Minute</b>");
sb.append("</td>");
sb.append("<td>");
sb.append("<b>Hour</b>");
sb.append("</td>");
sb.append("<td>");
sb.append("<b>Day of Month</b>");
sb.append("</td>");
sb.append("<td>");
sb.append("<b>Month</b>");
sb.append("</td>");
sb.append("<td>");
sb.append("<b>Day of Week</b>");
sb.append("</td>");
sb.append("</tr>");
for (AlarmEntry entry : alarms) {
ParsedAlarmEntry parsedAlarmEntry = this.parseAlarmEntryString(entry);
sb.append(parsedAlarmEntry.toFullHTML());
}
sb.append("</table>");
}
return sb.toString();
}
}