/// <summary> /// Executes this instance. /// </summary> public void Execute() { using ( var rockContext = new RockContext() ) { var jobService = new ServiceJobService( rockContext ); ServiceJob job = jobService.Get( JobId ); if ( job != null ) { try { // create a scheduler specific for the job var scheduleConfig = new System.Collections.Specialized.NameValueCollection(); var runNowSchedulerName = ( "RunNow:" + job.Guid.ToString( "N" ) ).Truncate( 40 ); scheduleConfig.Add( StdSchedulerFactory.PropertySchedulerInstanceName, runNowSchedulerName ); var schedulerFactory = new StdSchedulerFactory( scheduleConfig ); var sched = new StdSchedulerFactory( scheduleConfig ).GetScheduler(); if (sched.IsStarted) { // the job is currently running as a RunNow job return; } // create the quartz job and trigger IJobDetail jobDetail = jobService.BuildQuartzJob( job ); var jobTrigger = TriggerBuilder.Create() .WithIdentity( job.Guid.ToString(), job.Name ) .StartNow() .Build(); // schedule the job sched.ScheduleJob( jobDetail, jobTrigger ); // set up the listener to report back from the job when it completes sched.ListenerManager.AddJobListener( new RockJobListener(), EverythingMatcher<JobKey>.AllJobs() ); // start the scheduler sched.Start(); // Wait 10secs to give job chance to start Thread.Sleep( new TimeSpan( 0, 0, 10 ) ); // stop the scheduler when done with job sched.Shutdown( true ); } catch ( Exception ex ) { // create a friendly error message ExceptionLogService.LogException( ex, null ); string message = string.Format( "Error doing a 'Run Now' on job: {0}. \n\n{2}", job.Name, job.Assembly, ex.Message ); job.LastStatusMessage = message; job.LastStatus = "Error Loading Job"; rockContext.SaveChanges(); } } } }
/// <summary> /// Executes this instance. /// </summary> public void Execute() { using ( var rockContext = new RockContext() ) { var jobService = new ServiceJobService( rockContext ); ServiceJob job = jobService.Get( JobId ); if ( job != null ) { try { // create a scheduler var scheduleConfig = new System.Collections.Specialized.NameValueCollection(); scheduleConfig.Add( "org.quartz.scheduler.instanceName", "RunNow" ); var sched = new StdSchedulerFactory( scheduleConfig ).GetScheduler(); // create the quartz job and trigger IJobDetail jobDetail = jobService.BuildQuartzJob( job ); var jobTrigger = TriggerBuilder.Create() .WithIdentity( job.Guid.ToString(), job.Name ) .StartNow() .Build(); // schedule the job sched.ScheduleJob( jobDetail, jobTrigger ); // set up the listener to report back from the job when it completes sched.ListenerManager.AddJobListener( new RockJobListener(), EverythingMatcher<JobKey>.AllJobs() ); // start the scheduler sched.Start(); // Wait 10secs to give job chance to start Thread.Sleep( new TimeSpan( 0, 0, 10 ) ); // stop the scheduler when done with job sched.Shutdown( true ); } catch ( Exception ex ) { // create a friendly error message string message = string.Format( "Error loading the job: {0}. Ensure that the correct version of the job's assembly ({1}.dll) in the websites App_Code directory. \n\n\n\n{2}", job.Name, job.Assembly, ex.Message ); job.LastStatusMessage = message; job.LastStatus = "Error Loading Job"; rockContext.SaveChanges(); } } } }
/// <summary> /// Deletes the job. /// </summary> /// <param name="jobId">The job identifier.</param> public static void DeleteJob(int jobId) { using (var rockContext = new RockContext()) { var jobService = new ServiceJobService(rockContext); var job = jobService.Get(jobId); if (job != null) { jobService.Delete(job); rockContext.SaveChanges(); return; } } }
/// <summary> /// Handles the Delete event of the grdScheduledJobs control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="RowEventArgs" /> instance containing the event data.</param> protected void gScheduledJobs_Delete( object sender, RowEventArgs e ) { var rockContext = new RockContext(); var jobService = new ServiceJobService( rockContext ); ServiceJob job = jobService.Get( e.RowKeyId ); string errorMessage; if ( !jobService.CanDelete( job, out errorMessage ) ) { mdGridWarning.Show( errorMessage, ModalAlertType.Information ); return; } jobService.Delete( job ); rockContext.SaveChanges(); BindGrid(); }
/// <summary> /// Deletes job history items more than maximum. /// </summary> /// <param name="serviceJobId">The service job identifier.</param> public void DeleteMoreThanMax(int serviceJobId) { int historyCount; ServiceJobService serviceJobService = new ServiceJobService((RockContext)this.Context); ServiceJob serviceJob = serviceJobService.Get(serviceJobId); historyCount = serviceJob.HistoryCount; historyCount = historyCount <= 0 ? historyCount = 100 : historyCount; var matchingServiceJobs = this.AsNoFilter().Where(a => a.ServiceJobId == serviceJobId).OrderByDescending(a => a.StartDateTime); var serviceJobsMoreThanMax = matchingServiceJobs.Skip(historyCount).ToArray(); for (int i = 0; i < serviceJobsMoreThanMax.Count(); i++) { this.Delete(serviceJobsMoreThanMax[i]); } this.Context.SaveChanges(); }
/// <summary> /// Handles the Click event of the btnSave control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param> protected void btnSave_Click( object sender, EventArgs e ) { ServiceJob job; ServiceJobService jobService = new ServiceJobService(); int jobId = int.Parse( hfId.Value ); if ( jobId == 0 ) { job = new ServiceJob(); jobService.Add( job, CurrentPersonId ); } else { job = jobService.Get( jobId ); } job.Name = tbName.Text; job.Description = tbDescription.Text; job.IsActive = cbActive.Checked; job.Assembly = tbAssembly.Text; job.Class = tbClass.Text; job.NotificationEmails = tbNotificationEmails.Text; job.NotificationStatus = (JobNotificationStatus)int.Parse( drpNotificationStatus.SelectedValue ); job.CronExpression = tbCronExpression.Text; if ( !job.IsValid ) { // Controls will render the error messages return; } RockTransactionScope.WrapTransaction( () => { jobService.Save( job, CurrentPersonId ); } ); BindGrid(); pnlDetails.Visible = false; pnlGrid.Visible = true; }
/// <summary> /// Handles the Delete event of the grdScheduledJobs control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="RowEventArgs" /> instance containing the event data.</param> protected void gScheduledJobs_Delete( object sender, RowEventArgs e ) { RockTransactionScope.WrapTransaction( () => { var jobService = new ServiceJobService(); ServiceJob job = jobService.Get( (int)e.RowKeyValue ); string errorMessage; if ( !jobService.CanDelete( job, out errorMessage ) ) { mdGridWarning.Show( errorMessage, ModalAlertType.Information ); return; } jobService.Delete( job, CurrentPersonId ); jobService.Save( job, CurrentPersonId ); } ); BindGrid(); }
/// <summary> /// Handles the Click event of the btnSave control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param> protected void btnSave_Click( object sender, EventArgs e ) { try { ExpressionDescriptor.GetDescription( tbCronExpression.Text ); } catch (Exception ex) { tbCronExpression.ShowErrorMessage( "Invalid Cron Expression: " + ex.Message ); return; } ServiceJob job; var rockContext = new RockContext(); ServiceJobService jobService = new ServiceJobService( rockContext ); int jobId = int.Parse( hfId.Value ); if ( jobId == 0 ) { job = new ServiceJob(); jobService.Add( job ); } else { job = jobService.Get( jobId ); } job.Name = tbName.Text; job.Description = tbDescription.Text; job.IsActive = cbActive.Checked; if (job.Class != ddlJobTypes.SelectedValue) { job.Class = ddlJobTypes.SelectedValue; //// if the Class has changed, the current Assembly value might not match, //// so set the Assembly to null to have Rock figure it out automatically job.Assembly = null; } job.NotificationEmails = tbNotificationEmails.Text; job.NotificationStatus = (JobNotificationStatus)int.Parse( ddlNotificationStatus.SelectedValue ); job.CronExpression = tbCronExpression.Text; if ( !job.IsValid ) { // Controls will render the error messages return; } rockContext.WrapTransaction( () => { rockContext.SaveChanges(); job.LoadAttributes( rockContext ); Rock.Attribute.Helper.GetEditValues( phAttributes, job ); job.SaveAttributeValues( rockContext ); } ); NavigateToParentPage(); }
/// <summary> /// Handles the Click event of the btnSave control. /// </summary> /// <param name="sender">The source of the event.</param> /// <param name="e">The <see cref="EventArgs" /> instance containing the event data.</param> protected void btnSave_Click( object sender, EventArgs e ) { ServiceJob job; var rockContext = new RockContext(); ServiceJobService jobService = new ServiceJobService( rockContext ); int jobId = int.Parse( hfId.Value ); if ( jobId == 0 ) { job = new ServiceJob(); jobService.Add( job ); } else { job = jobService.Get( jobId ); } job.Name = tbName.Text; job.Description = tbDescription.Text; job.IsActive = cbActive.Checked; job.Class = ddlJobTypes.SelectedValue; job.NotificationEmails = tbNotificationEmails.Text; job.NotificationStatus = (JobNotificationStatus)int.Parse( ddlNotificationStatus.SelectedValue ); job.CronExpression = tbCronExpression.Text; if ( !job.IsValid ) { // Controls will render the error messages return; } rockContext.WrapTransaction( () => { rockContext.SaveChanges(); job.LoadAttributes( rockContext ); Rock.Attribute.Helper.GetEditValues( phAttributes, job ); job.SaveAttributeValues( rockContext ); } ); NavigateToParentPage(); }
/// <summary> /// Called by the <see cref="IScheduler"/> when a <see cref="IJobDetail"/> /// is about to be executed (an associated <see cref="ITrigger"/> /// has occurred). /// <para> /// This method will not be invoked if the execution of the Job was vetoed /// by a <see cref="ITriggerListener"/>. /// </para> /// </summary> /// <param name="context"></param> /// <seealso cref="JobExecutionVetoed(IJobExecutionContext)"/> public void JobToBeExecuted( IJobExecutionContext context ) { StringBuilder message = new StringBuilder(); // get job type id int jobId = context.JobDetail.Description.AsInteger(); // load job var rockContext = new RockContext(); var jobService = new ServiceJobService( rockContext ); var job = jobService.Get( jobId ); if (job != null && job.Guid != Rock.SystemGuid.ServiceJob.JOB_PULSE.AsGuid()) { job.LastStatus = "Running"; job.LastStatusMessage = "Started at " + RockDateTime.Now.ToString(); rockContext.SaveChanges(); } }
/// <summary> /// Called by the <see cref="IScheduler"/> after a <see cref="IJobDetail"/> /// has been executed, and before the associated <see cref="Quartz.Spi.IOperableTrigger"/>'s /// <see cref="Quartz.Spi.IOperableTrigger.Triggered"/> method has been called. /// </summary> /// <param name="context"></param> /// <param name="jobException"></param> public void JobWasExecuted( IJobExecutionContext context, JobExecutionException jobException ) { StringBuilder message = new StringBuilder(); bool sendMessage = false; // get job type id int jobId = Convert.ToInt16( context.JobDetail.Description ); // load job var rockContext = new RockContext(); var jobService = new ServiceJobService( rockContext ); var job = jobService.Get( jobId ); // format the message message.Append( string.Format( "The job {0} ran for {1} seconds on {2}. Below is the results:<p>", job.Name, context.JobRunTime.TotalSeconds, context.FireTimeUtc.Value.DateTime.ToLocalTime() ) ); // if noticiation staus is all set flag to send message if ( job.NotificationStatus == JobNotificationStatus.All ) { sendMessage = true; } // set last run date job.LastRunDateTime = RockDateTime.Now; // context.FireTimeUtc.Value.DateTime.ToLocalTime(); // set run time job.LastRunDurationSeconds = Convert.ToInt32( context.JobRunTime.TotalSeconds ); // set the scheduler name job.LastRunSchedulerName = context.Scheduler.SchedulerName; // determine if an error occured if ( jobException == null ) { job.LastSuccessfulRunDateTime = job.LastRunDateTime; job.LastStatus = "Success"; if ( context.Result is string ) { job.LastStatusMessage = context.Result as string; } message.Append( "Result: Success" ); // determine if message should be sent if ( job.NotificationStatus == JobNotificationStatus.Success ) { sendMessage = true; } } else { Exception exceptionToLog = jobException; // drill down to the interesting exception while ( exceptionToLog is Quartz.SchedulerException && exceptionToLog.InnerException != null ) { exceptionToLog = exceptionToLog.InnerException; } // log the exception to the database ExceptionLogService.LogException( exceptionToLog, null ); var summaryException = exceptionToLog; // put the exception into the status job.LastStatus = "Exception"; AggregateException aggregateException = summaryException as AggregateException; if ( aggregateException != null && aggregateException.InnerExceptions != null && aggregateException.InnerExceptions.Count == 1 ) { // if it's an aggregate, but there is only one, convert it to a single exception summaryException = aggregateException.InnerExceptions[0]; aggregateException = null; } if ( aggregateException != null ) { var firstException = aggregateException.InnerExceptions.First(); job.LastStatusMessage = "One or more exceptions occurred. First Exception: " + firstException.Message; summaryException = firstException; } else { job.LastStatusMessage = summaryException.Message; } message.Append( "Result: Exception<p>Message:<br>" + job.LastStatusMessage ); if ( summaryException.InnerException != null ) { job.LastStatusMessage += " (" + summaryException.InnerException.Message + ")"; message.Append( "<p>Inner Exception:<br>" + summaryException.InnerException.Message ); } if ( job.NotificationStatus == JobNotificationStatus.Error ) { sendMessage = true; } } rockContext.SaveChanges(); // send notification if ( sendMessage ) { // TODO: implement email send once it's available } }
/// <summary> /// Method that will be run at Rock startup /// </summary> public void OnStartup() { /* 10-01-2021 MDP * * In multi-server/cluster/web-farm Rock environments, only one of the servers should be running * these DataMigrationsStartups. To make sure that multiple servers don't run these, we'll * check if this is the server with RunJobsInIISContext enabled. * * If RunJobsInIISContext isn't enabled on this server, don't run these. However, if this * is a developer environment, we'll want to run these regardless of the RunJobsInIISContext setting. * */ bool runJobsInContext = Convert.ToBoolean(ConfigurationManager.AppSettings["RunJobsInIISContext"]); if (!runJobsInContext) { // RunJobsInIISContext isn't enabled on this server, so don't run these DataMigrationsStartups unless // this is a developer environment if (!System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment) { // RunJobsInIISContext isn't enabled, and this isn't a developer environment so exit without running DataMigrationsStartups. return; } } List <Guid> runOnceJobGuids = new List <Guid> { SystemGuid.ServiceJob.DATA_MIGRATIONS_74.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_80.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_84.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_90_DISC.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_90.AsGuid(), SystemGuid.ServiceJob.MIGRATE_HISTORY_SUMMARY_DATA.AsGuid(), SystemGuid.ServiceJob.MIGRATE_ATTENDANCE_OCCURRENCE.AsGuid(), SystemGuid.ServiceJob.MIGRATE_FAMILY_CHECKIN_IDS.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_90_SCHEDULEDTRANSACTIONNOTESTOHISTORY.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_100_ATTRIBUTEVALUE_VALUEASNUMERIC.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_100_SUNDAYDATE.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_103_SPIRITUAL_GIFTS.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_110_POPULATE_DATE_KEYS.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_110_COMMUNICATIONRECIPIENT_RESPONSECODE_INDEX.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_110_POPULATE_RELATED_DATAVIEW_ID.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_120_UPDATE_INTERACTION_INDEXES.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_120_ADD_COMMUNICATIONRECIPIENT_INDEX.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_120_ADD_COMMUNICATION_GET_QUEUED_INDEX.AsGuid(), /* MDP 07-22-2021 * * NOTE: We intentionally are excluding SystemGuid.ServiceJob.DATA_MIGRATIONS_122_INTERACTION_PERSONAL_DEVICE_ID * from DataMigrationStartup and will just wait for it to run at 2am. * See https://app.asana.com/0/0/1199506067368201/f * */ SystemGuid.ServiceJob.DATA_MIGRATIONS_124_UPDATE_GROUP_SALUTATIONS.AsGuid(), SystemGuid.ServiceJob.POST_INSTALL_DATA_MIGRATIONS.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_124_DECRYPT_FINANCIAL_PAYMENT_DETAILS.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_125_UPDATE_STEP_PROGRAM_COMPLETION.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_125_ADD_COMMUNICATION_SYSTEM_COMMUNICATION_ID_INDEX.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_127_REBUILD_GROUP_SALUTATIONS.AsGuid(), SystemGuid.ServiceJob.DATA_MIGRATIONS_130_ADD_INTERACTION_INTERACTION_COMPONENT_ID_INDEX.AsGuid() }; // run any of the above jobs if they still exist (they haven't run and deleted themselves) var runOnceJobIds = new Model.ServiceJobService(new Rock.Data.RockContext()).Queryable().Where(a => runOnceJobGuids.Contains(a.Guid)).Select(a => a.Id).ToList(); // start a task that will run any incomplete RunOneJobs (one at a time) Task.Run(() => { var rockContext = new Rock.Data.RockContext(); var jobService = new Rock.Model.ServiceJobService(rockContext); foreach (var runOnceJobId in runOnceJobIds) { try { var job = jobService.Get(runOnceJobId); jobService.RunNow(job, out _); } catch (Exception ex) { // this shouldn't happen since the jobService.RunNow catches and logs errors, but just in case ExceptionLogService.LogException(ex); } } }); }
/// <summary> /// Called by the <see cref="IScheduler"/> after a <see cref="IJobDetail"/> /// has been executed, and before the associated <see cref="Quartz.Spi.IOperableTrigger"/>'s /// <see cref="Quartz.Spi.IOperableTrigger.Triggered"/> method has been called. /// </summary> /// <param name="context"></param> /// <param name="jobException"></param> public void JobWasExecuted( IJobExecutionContext context, JobExecutionException jobException ) { StringBuilder message = new StringBuilder(); bool sendMessage = false; // get job type id int jobId = Convert.ToInt16(context.JobDetail.Description); // load job ServiceJobService jobService = new ServiceJobService(); ServiceJob job = jobService.Get(jobId); // format the message message.Append( String.Format( "The job {0} ran for {1} seconds on {2}. Below is the results:<p>" , job.Name, context.JobRunTime.TotalSeconds, context.FireTimeUtc.Value.DateTime.ToLocalTime()) ); // if noticiation staus is all set flag to send message if ( job.NotificationStatus == JobNotificationStatus.All ) sendMessage = true; // set last run date job.LastRunDateTime = context.FireTimeUtc.Value.DateTime.ToLocalTime(); // set run time job.LastRunDurationSeconds = Convert.ToInt32(context.JobRunTime.TotalSeconds); // set the scheduler name job.LastRunSchedulerName = context.Scheduler.SchedulerName; // determine if an error occured if ( jobException == null ) { job.LastSuccessfulRunDateTime = job.LastRunDateTime; job.LastStatus = "Success"; job.LastStatusMessage = ""; message.Append( "Result: Success" ); // determine if message should be sent if ( job.NotificationStatus == JobNotificationStatus.Success ) sendMessage = true; } else { // put the exception into the status job.LastStatus = "Exception"; job.LastStatusMessage = jobException.Message; message.Append( "Result: Exception<p>Message:<br>" + jobException.Message ); if ( jobException.InnerException != null ) { job.LastStatusMessage += " Inner Exception: " + jobException.InnerException.Message; message.Append( "<p>Inner Exception:<br>" + jobException.InnerException.Message ); } if ( job.NotificationStatus == JobNotificationStatus.Error ) sendMessage = true; } jobService.Save( job, null ); // send notification if ( sendMessage ) { // TODO: implement email send once it's available } }