public Activity(IDataReader reader) { Id = reader.SafeGet <int>("ID"); Label = reader.SafeGet <string>("LABEL", String.Empty); LabelLang = reader.SafeGet <string>("LABEL_LANG", String.Empty); Details = reader.SafeGet <string>("INBOX_DETAIL", String.Empty); DueDate = reader.SafeGet <DateTime>("DUE_DATE", BuiltInFunction.NullDate()); IsOpened = reader.SafeGet <bool>("OPENED"); IsSeen = reader.SafeGet <bool>("SEEN"); }
public static bool IsRunnableActivity(DateTime isRunningSince, ActivityStatus?forInitialStatus, ActivityStatus currentStatus, DateTime previousNextRun, DateTime nextRun) { //#605602 - Ensure the activity is not already running "isRunningSince" //Ensure the conditions that made the scheduler request the activity to run have not changed. "currentStatus" and "nextRun" bool runnable = (isRunningSince == BuiltInFunction.NullDate()) && (!forInitialStatus.HasValue || forInitialStatus.Value == currentStatus) && (previousNextRun == BuiltInFunction.NullDate() || //#1188860 - To avoid issues with daylight saving time, use universal time to compare the two date times (Math.Abs((previousNextRun.ToUniversalTime() - nextRun.ToUniversalTime()).TotalMilliseconds) < 1)); return(runnable); }
/// <summary> /// Returns (currently not used): 1 if scheduler should retry later; 0 otherwise /// </summary> /// <param name="heContext"></param> /// <param name="timeout"></param> /// <returns></returns> public static int Execute(HeContext heContext, int timeout) { string schedule; int cyclicJobId; bool isShared; DateTime dbNow, newNextRun; int eSpaceId = Global.eSpaceId; int tenantId = heContext.AppInfo.Tenant.Id; string errorLogId = ""; DateTime dbStartTime, lastRun, nextRun, previousNextRun, isRunnSince; DateTime localStartedDateTime = DateTime.Now; // used to measure the timer duration DateTime newIsRunningSince, currentIsRunningSince; int duration = 0; string isRunningBy; int numberOfTries = 0; int maxNumberOfRetries = RuntimePlatformSettings.Timers.NumberOfRetries.GetValue(); // ExceptionLogged signals that this type of exception has been logged bool ExceptionLogged = false; try { try { using (Transaction mainTrans = DatabaseAccess.ForRuntimeDatabase.GetRequestTransaction()) { dbStartTime = DBRuntimePlatform.Instance.GetDbDatetime(mainTrans); cyclicJobId = DBRuntimePlatform.Instance.GetCyclicJobId(mainTrans, eSpaceId, tenantId, TimerKey, out isShared); } using (Transaction privTrans = DatabaseAccess.ForRuntimeDatabase.GetCommitableTransaction()) { try { bool ok = DBRuntimePlatform.Instance.GetCyclicJobForUpdate(privTrans, cyclicJobId, isShared, out isRunnSince, out schedule, out lastRun, out previousNextRun, out isRunningBy); if (!ok) { throw new DataBaseException("Unable to get data for Bootstrap Timer (" + cyclicJobId + ") in Execute method."); } // return if not yet time to execute if (dbStartTime.CompareTo(previousNextRun) < 0) { return(0); } // some node is executing it if (isRunnSince != BuiltInFunction.NullDate()) { // In normal cases, it will return from execution because // some other node is executing this timer if (dbStartTime.CompareTo(isRunnSince.AddSeconds(1.2 * timeout)) < 0) { return(0); } // recover mechanism: if it is executing for too long // maybe there was a problem with the node, but the database // was left with data stating it is still executing. TimeSpan ts; string recoverDetail = ""; try { string isRunnBy = "?"; ts = (dbStartTime - isRunnSince); recoverDetail = "Marked as running since " + isRunnSince.ToString() + " (" + ts.TotalMinutes.ToString() + " minutes) by hubnode '" + isRunnBy + "'. Timeout defined is " + (timeout / 60.0).ToString() + " minutes"; } catch {} ErrorLog.LogApplicationError("Recovering timer Bootstrap execution. It is marked as running for too long", recoverDetail, heContext, ""); } // lets update db, stating that I'm executing the job numberOfTries = DBRuntimePlatform.Instance.GetNumberOfTries(privTrans, cyclicJobId, isShared); if (numberOfTries >= maxNumberOfRetries) { DBRuntimePlatform.Instance.GetTimerScheduleNextRunAndDbDate(privTrans, cyclicJobId, isShared, out schedule, out dbNow, out newNextRun, out currentIsRunningSince); RuntimeScheduler.NextRun(schedule, dbNow, out nextRun); DBRuntimePlatform.Instance.SetTimerNextRun(privTrans, cyclicJobId, isShared, nextRun); errorLogId = ErrorLog.StaticWrite(DateTime.Now, heContext.Session.SessionID, heContext.AppInfo.eSpaceId, heContext.AppInfo.Tenant.Id, heContext.Session.UserId, String.Format("Timer Bootstrap reached maximum number of retries ({0}) [Calculating next run]", maxNumberOfRetries), "", ""); duration = (int)((TimeSpan)(DateTime.Now - localStartedDateTime)).TotalSeconds; // lets update the rest of the cyclic job stuff... DBRuntimePlatform.Instance.SetTimerLastRun(privTrans, cyclicJobId, isShared, dbStartTime, duration); CyclicJobLog jobLog = new CyclicJobLog(); jobLog.Write(dbStartTime, duration, TimerKey, heContext.AppInfo.eSpaceId, heContext.AppInfo.Tenant.Id, RuntimeEnvironment.MachineName, errorLogId, previousNextRun, nextRun, heContext.AppInfo.eSpaceName, heContext.AppInfo.ApplicationName, heContext.AppInfo.ApplicationUIDAsKey, TimerName); return(0); } DBRuntimePlatform.Instance.SetTimerRunningBy(privTrans, cyclicJobId, isShared, RuntimeEnvironment.MachineName, out newIsRunningSince); DBRuntimePlatform.Instance.IncrementNumberOfTries(privTrans, cyclicJobId, isShared); } finally { privTrans.Commit(); // NOTE: Must release and reget the session... the timer action might commit or something! } } } catch (Exception e) { duration = (int)((TimeSpan)(DateTime.Now - localStartedDateTime)).TotalSeconds; errorLogId = ErrorLog.LogApplicationError("Timer Bootstrap error (before executing action 'BootstrapContacts'). Timer duration = " + duration + " secs: " + e.Message + " [Will retry later]", e.ToString(), heContext, ""); ExceptionLogged = true; return(1); // assume some error in update, so it will retry later... } // from tests in framework 1.0, it seems this ScriptTimeout is not working as expected... // but it seems in framework 1.1 it works fine (if not in debug mode) heContext.Context.Server.ScriptTimeout = timeout; try { Actions.ActionBootstrapContacts(heContext); } catch (Exception e) { // error in timer action: we must rollback all pending transactions DatabaseAccess.RollbackAllTransactions(); using (Transaction mainTrans = DatabaseAccess.ForRuntimeDatabase.GetRequestTransaction()) { if (!ExceptionLogged) // if already logged, don't log the same error { duration = (int)((TimeSpan)(DateTime.Now - localStartedDateTime)).TotalSeconds; currentIsRunningSince = DBRuntimePlatform.Instance.GetTimerRunningSince(mainTrans, cyclicJobId, isShared); if (currentIsRunningSince != newIsRunningSince) { ErrorLog.LogApplicationError("Timer 'Bootstrap' Running Since Date changed unexpectedly. Expected '" + BuiltInFunction.DateTimeToText(newIsRunningSince) + "' but was '" + BuiltInFunction.DateTimeToText(currentIsRunningSince) + "'.", "", heContext, ""); } DBRuntimePlatform.Instance.ClearTimerRunningBy(mainTrans, cyclicJobId, isShared); ExceptionLogged = true; errorLogId = ErrorLog.LogApplicationError("Timer Bootstrap error (inside action 'BootstrapContacts'). Timer duration = " + duration + " secs:" + e.Message + String.Format(" [retry {0} of {1} scheduled]", numberOfTries + 1, maxNumberOfRetries), e.ToString(), heContext, ""); CyclicJobLog jobLog = new CyclicJobLog(); jobLog.Write( dbStartTime, duration, TimerKey, heContext.AppInfo.eSpaceId, heContext.AppInfo.Tenant.Id, RuntimeEnvironment.MachineName, errorLogId, previousNextRun, previousNextRun, heContext.AppInfo.eSpaceName, heContext.AppInfo.ApplicationName, heContext.AppInfo.ApplicationUIDAsKey, TimerName); return(0); } } } using (Transaction mainTrans = DatabaseAccess.ForRuntimeDatabase.GetRequestTransaction()) { try { // calculate next run based on current date in db server and also // on scheduler (that may have been changed inside the user action // for example) DBRuntimePlatform.Instance.GetTimerScheduleNextRunAndDbDate(mainTrans, cyclicJobId, isShared, out schedule, out dbNow, out newNextRun, out currentIsRunningSince); RuntimeScheduler.NextRun(schedule, dbNow, out nextRun); if (newNextRun == previousNextRun) { DBRuntimePlatform.Instance.SetTimerNextRun(mainTrans, cyclicJobId, isShared, nextRun); } else { nextRun = newNextRun; } duration = (int)((TimeSpan)(DateTime.Now - localStartedDateTime)).TotalSeconds; if (currentIsRunningSince != newIsRunningSince) { ErrorLog.LogApplicationError("Timer 'Bootstrap' Running Since Date changed unexpectedly. Expected '" + BuiltInFunction.DateTimeToText(newIsRunningSince) + "' but was '" + BuiltInFunction.DateTimeToText(currentIsRunningSince) + "'.", "", heContext, ""); } // lets update the rest of the cyclic job stuff... DBRuntimePlatform.Instance.SetTimerLastRun(mainTrans, cyclicJobId, isShared, dbStartTime, duration); DBRuntimePlatform.Instance.ResetNumberOfTries(mainTrans, cyclicJobId, isShared); CyclicJobLog jobLog = new CyclicJobLog(); jobLog.Write( dbStartTime, duration, TimerKey, heContext.AppInfo.eSpaceId, heContext.AppInfo.Tenant.Id, RuntimeEnvironment.MachineName, errorLogId, previousNextRun, nextRun, heContext.AppInfo.eSpaceName, heContext.AppInfo.ApplicationName, heContext.AppInfo.ApplicationUIDAsKey, TimerName); return(0); } catch (Exception e) { if (!ExceptionLogged) // if already logged, don't log the same error { duration = (int)((TimeSpan)(DateTime.Now - localStartedDateTime)).TotalSeconds; errorLogId = ErrorLog.LogApplicationError("Timer Bootstrap error (after executing action 'BootstrapContacts'). Timer duration = " + duration + " secs: " + e.Message + " [Will retry later]", e.ToString(), heContext, ""); ExceptionLogged = true; } return(1); // assume some error in update, so it will retry later... } } } catch (Exception e) { if (!ExceptionLogged) // if already logged, don't log the same error { duration = (int)((TimeSpan)(DateTime.Now - localStartedDateTime)).TotalSeconds; errorLogId = ErrorLog.LogApplicationError("Timer Bootstrap error. Timer duration = " + duration + " secs: " + e.Message + " [Will retry later]", e.ToString(), heContext, ""); ExceptionLogged = true; } return(1); } }
public System.IAsyncResult BeginExecuteActivityAction(string ssKey, int activityId, int processId, int tenantId, ActivityStatus forInitialStatus, System.AsyncCallback callback, object asyncState) { return(BeginExecuteActivityActionv2(ssKey, activityId, processId, tenantId, forInitialStatus, BuiltInFunction.NullDate(), callback, asyncState)); }
// Execute Activity Action public bool ExecuteActivityAction(string ssKey, int activityId, int processId, int tenantId, ActivityStatus forInitialStatus) { return(ExecuteActivityActionv2(ssKey, activityId, processId, tenantId, forInitialStatus, BuiltInFunction.NullDate())); }
protected void StartActivityExecution(HeContext heContext, int activityId, int processId, ActivityStatus?forInitialStatus, out bool runnable, out ActivityStatus currentStatus, DateTime previousNextRun) { runnable = false; using (Transaction trans = DatabaseAccess.ForSystemDatabase.GetRequestTransaction()) { int tenantId = heContext.AppInfo.Tenant.Id; int precedentActivityId; int precedentActivitySuccessorsCount; int rootProcessId; if (Debugger.IsRunning) { rootProcessId = DBRuntimePlatform.Instance.GetTopProcessId(trans, processId); precedentActivityId = DBRuntimePlatform.Instance.GetPrecedentActivityId(trans, activityId); if (precedentActivityId == BuiltInFunction.NullIdentifier() && rootProcessId != processId) { // the precendent activity is the activity that launched the subprocess. We need to get // an activity from the same eSpace we're in, because external processes are not executed // in the same web app (contrary to what happens when debugging reference actions, whose code // is all in the consumer eSpace) precedentActivityId = DBRuntimePlatform.Instance.GetAncestorActivityIdInTenant(trans, processId, RunningInfo.TenantId); } precedentActivitySuccessorsCount = DBRuntimePlatform.Instance.GetActivitySuccessorsCount(trans, precedentActivityId); } else { rootProcessId = 0; precedentActivityId = -1; precedentActivitySuccessorsCount = 0; } SetCurrentProcessInfo(processId, activityId, precedentActivityId, precedentActivitySuccessorsCount, rootProcessId); if (DBRuntimePlatform.Instance.GetProcessStatus(trans, processId) == ProcessStatus.Suspended) { throw new InvalidOperationException("Could not execute a built-in/extended action on activity '" + Name + "' (#" + activityId + ") because its process is suspended."); } using (IDataReader reader = DBRuntimePlatform.Instance.GetActivity(trans, tenantId, activityId, true)) { if (reader.Read()) { DateTime nextRun = (DateTime)reader.SafeGet <DateTime>("NEXT_RUN"); currentStatus = (ActivityStatus)reader.SafeGet <int>("STATUS_ID"); DateTime isRunningSince = reader.SafeGet <DateTime>("IS_RUNNING_SINCE", BuiltInFunction.NullDate()); runnable = IsRunnableActivity(isRunningSince, forInitialStatus, currentStatus, previousNextRun, nextRun); } else { throw new InvalidOperationException("Could not execute a built-in/extended action on activity '" + Name + "' (#" + activityId + ") because the activity does not exist."); } } if (runnable) { DBRuntimePlatform.Instance.UpdateActivity(trans, tenantId, activityId, null, null, null, null, null, null, null, null, true, Settings.MachineName, null, null, null, null, null, null, null, null, null); } } DatabaseAccess.CommitAllTransactions(); }
protected void StartActivityExecution(HeContext heContext, int activityId, int processId, ActivityStatus?forInitialStatus, out bool runnable, out ActivityStatus currentStatus) { StartActivityExecution(heContext, activityId, processId, null, out runnable, out currentStatus, BuiltInFunction.NullDate()); }