public override bool Execute( RockContext rockContext, WorkflowAction action, Object entity, out List<string> errorMessages ) { errorMessages = new List<string>(); var JobGuid = GetAttributeValue( action, "Job" ).AsGuid(); if ( !JobGuid.IsEmpty() ) { ServiceJob Job = new ServiceJobService( rockContext ).Get( JobGuid ); if ( Job != null ) { var transaction = new Rock.Transactions.RunJobNowTransaction( Job.Id ); // Process the transaction on another thread System.Threading.Tasks.Task.Run( () => transaction.Execute() ); action.AddLogEntry( string.Format( "The '{0}' job has been started.", Job.Name ) ); return true; } } errorMessages.Add("The specified Job could not be found"); return false; }
/// <summary> /// Updates the scheduled jobs. /// </summary> /// <param name="context">The context.</param> private void UpdateScheduledJobs( IJobExecutionContext context ) { var scheduler = context.Scheduler; var rockContext = new Rock.Data.RockContext(); ServiceJobService jobService = new ServiceJobService( rockContext ); List<ServiceJob> activeJobList = jobService.GetActiveJobs().ToList(); List<Quartz.JobKey> scheduledQuartzJobs = scheduler.GetJobKeys( GroupMatcher<JobKey>.GroupStartsWith( string.Empty ) ).ToList(); // delete any jobs that are no longer exist (are are not set to active) in the database var quartsJobsToDelete = scheduledQuartzJobs.Where( a => !activeJobList.Any( j => j.Guid.Equals( a.Name.AsGuid() ) ) ); foreach ( JobKey jobKey in quartsJobsToDelete ) { scheduler.DeleteJob( jobKey ); } // add any jobs that are not yet scheduled var newActiveJobs = activeJobList.Where( a => !scheduledQuartzJobs.Any( q => q.Name.AsGuid().Equals( a.Guid ) ) ); foreach ( Rock.Model.ServiceJob job in newActiveJobs ) { try { IJobDetail jobDetail = jobService.BuildQuartzJob( job ); ITrigger jobTrigger = jobService.BuildQuartzTrigger( job ); scheduler.ScheduleJob( jobDetail, jobTrigger ); } 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(); // reload the jobs in case any where added/removed scheduledQuartzJobs = scheduler.GetJobKeys( GroupMatcher<JobKey>.GroupStartsWith( string.Empty ) ).ToList(); // update schedule if the schedule has changed foreach ( var jobKey in scheduledQuartzJobs ) { var jobCronTrigger = scheduler.GetTriggersOfJob( jobKey ).OfType<ICronTrigger>().FirstOrDefault(); if ( jobCronTrigger != null ) { var activeJob = activeJobList.FirstOrDefault( a => a.Guid.Equals( jobKey.Name.AsGuid() ) ); if ( activeJob != null ) { if ( activeJob.CronExpression != jobCronTrigger.CronExpressionString ) { ITrigger newJobTrigger = jobService.BuildQuartzTrigger( activeJob ); scheduler.RescheduleJob( jobCronTrigger.Key, newJobTrigger ); } } } } }
/// <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(); } } } }
protected override void OnStart( string[] args ) { ISchedulerFactory sf; // create scheduler sf = new StdSchedulerFactory(); sched = sf.GetScheduler(); // get list of active jobs ServiceJobService jobService = new ServiceJobService(); foreach ( ServiceJob job in jobService.GetActiveJobs().ToList() ) { try { IJobDetail jobDetail = jobService.BuildQuartzJob( job ); ITrigger jobTrigger = jobService.BuildQuartzTrigger( job ); sched.ScheduleJob( jobDetail, jobTrigger ); } catch ( Exception ex ) { // get path to the services directory String path = System.Reflection.Assembly.GetExecutingAssembly().Location; path = System.IO.Path.GetDirectoryName(path); // 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 services directory ({2}) of your server.", job.Name, job.Assembly, path); message = message + "\n\n\n\n" + ex.Message; //throw new JobLoadFailedException( message ); job.LastStatusMessage = message; job.LastStatus = "Error Loading Job"; jobService.Save( job, null ); } } // set up the listener to report back from jobs as they complete sched.ListenerManager.AddJobListener( new RockJobListener(), EverythingMatcher<JobKey>.AllJobs() ); // start the scheduler sched.Start(); }
/// <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 Start event of the Application 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 Application_Start( object sender, EventArgs e ) { try { LogMessage( APP_LOG_FILENAME, "Application Starting..." ); DateTime startDateTime = RockDateTime.Now; if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { System.Diagnostics.Debug.WriteLine( string.Format( "Application_Start: {0}", RockDateTime.Now.ToString( "hh:mm:ss.FFF" ) ) ); } // Clear all cache RockMemoryCache.Clear(); // Get a db context using ( var rockContext = new RockContext() ) { //// Run any needed Rock and/or plugin migrations //// NOTE: MigrateDatabase must be the first thing that touches the database to help prevent EF from creating empty tables for a new database MigrateDatabase( rockContext ); // Preload the commonly used objects LoadCacheObjects( rockContext ); // Run any plugin migrations MigratePlugins( rockContext ); if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { try { new AttributeService( rockContext ).Get( 0 ); System.Diagnostics.Debug.WriteLine( string.Format( "ConnectToDatabase - {0} ms", ( RockDateTime.Now - startDateTime ).TotalMilliseconds ) ); startDateTime = RockDateTime.Now; } catch { // Intentionally Blank } } RegisterRoutes( rockContext, RouteTable.Routes ); // Configure Rock Rest API GlobalConfiguration.Configure( Rock.Rest.WebApiConfig.Register ); if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { System.Diagnostics.Debug.WriteLine( string.Format( "LoadCacheObjects - {0} ms", ( RockDateTime.Now - startDateTime ).TotalMilliseconds ) ); startDateTime = RockDateTime.Now; } // setup and launch the jobs infrastructure if running under IIS bool runJobsInContext = Convert.ToBoolean( ConfigurationManager.AppSettings["RunJobsInIISContext"] ); if ( runJobsInContext ) { ISchedulerFactory sf; // create scheduler sf = new StdSchedulerFactory(); sched = sf.GetScheduler(); // get list of active jobs ServiceJobService jobService = new ServiceJobService( rockContext ); foreach ( ServiceJob job in jobService.GetActiveJobs().ToList() ) { try { IJobDetail jobDetail = jobService.BuildQuartzJob( job ); ITrigger jobTrigger = jobService.BuildQuartzTrigger( job ); sched.ScheduleJob( jobDetail, jobTrigger ); } 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(); } } // set up the listener to report back from jobs as they complete sched.ListenerManager.AddJobListener( new RockJobListener(), EverythingMatcher<JobKey>.AllJobs() ); // start the scheduler sched.Start(); } // Force the static Liquid class to get instantiated so that the standard filters are loaded prior // to the custom RockFilter. This is to allow the custom 'Date' filter to replace the standard // Date filter. Liquid.UseRubyDateFormat = false; //// NOTE: This means that template filters will also use CSharpNamingConvention //// For example the dotliquid documentation says to do this for formatting dates: //// {{ some_date_value | date:"MMM dd, yyyy" }} //// However, if CSharpNamingConvention is enabled, it needs to be: //// {{ some_date_value | Date:"MMM dd, yyyy" }} Template.NamingConvention = new DotLiquid.NamingConventions.CSharpNamingConvention(); Template.FileSystem = new RockWeb.LavaFileSystem(); Template.RegisterSafeType( typeof( Enum ), o => o.ToString() ); Template.RegisterFilter( typeof( Rock.Lava.RockFilters ) ); // add call back to keep IIS process awake at night and to provide a timer for the queued transactions AddCallBack(); Rock.Security.Authorization.Load(); } EntityTypeService.RegisterEntityTypes( Server.MapPath( "~" ) ); FieldTypeService.RegisterFieldTypes( Server.MapPath( "~" ) ); BundleConfig.RegisterBundles( BundleTable.Bundles ); // mark any user login stored as 'IsOnline' in the database as offline MarkOnlineUsersOffline(); SqlServerTypes.Utilities.LoadNativeAssemblies( Server.MapPath( "~" ) ); LogMessage( APP_LOG_FILENAME, "Application Started Succesfully" ); } catch (Exception ex) { SetError66(); throw ( new Exception( "Error occurred during application startup", ex ) ); } }
/// <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> /// Starts the job scheduler. /// </summary> public void StartJobScheduler() { if ( !System.IO.File.Exists( "web.connectionstrings.config" ) ) { // Write an eventlog about web.connectionstring.config not found this.EventLog.WriteEntry( "Unable to find web.connectionstrings.config", EventLogEntryType.Error ); } ISchedulerFactory sf; // create scheduler sf = new StdSchedulerFactory(); sched = sf.GetScheduler(); var rockContext = new RockContext(); // get list of active jobs ServiceJobService jobService = new ServiceJobService( rockContext ); List<ServiceJob> activeJobs = null; try { // make sure that we can connect to the database and get the jobs list. Write a good EventLog message and exit the app if we can't this.EventLog.WriteEntry( string.Format( "Connecting to database {0}:{1}", rockContext.Database.Connection.DataSource, rockContext.Database.Connection.Database ), EventLogEntryType.Information ); rockContext.Database.Connection.Open(); activeJobs = jobService.GetActiveJobs().ToList(); } catch ( Exception ex ) { this.EventLog.WriteEntry( "Unable load active jobs list. " + ex.Message, EventLogEntryType.Error ); throw ex; } foreach ( ServiceJob job in activeJobs ) { const string errorLoadingStatus = "Error Loading Job"; try { IJobDetail jobDetail = jobService.BuildQuartzJob( job ); ITrigger jobTrigger = jobService.BuildQuartzTrigger( job ); sched.ScheduleJob( jobDetail, jobTrigger ); //// if the last status was an error, but we now loaded successful, clear the error // also, if the last status was 'Running', clear that status because it would have stopped if the app restarted if ( job.LastStatus == errorLoadingStatus || job.LastStatus == "Running" ) { job.LastStatusMessage = string.Empty; job.LastStatus = string.Empty; rockContext.SaveChanges(); } } catch ( Exception ex ) { ExceptionLogService.LogException( ex, null ); // get path to the services directory string path = System.Reflection.Assembly.GetExecutingAssembly().Location; path = System.IO.Path.GetDirectoryName( path ); // create the error message string message = string.Format( "Error loading the job: {0}.\n\n{1}\n\n Job Assembly: {2}, Path: {3}", job.Name, ex.Message, job.Assembly, path ); job.LastStatusMessage = message; job.LastStatus = errorLoadingStatus; rockContext.SaveChanges(); } // set up the listener to report back from jobs as they complete sched.ListenerManager.AddJobListener( new RockJobListener(), EverythingMatcher<JobKey>.AllJobs() ); // start the scheduler sched.Start(); } // set up the listener to report back from jobs as they complete sched.ListenerManager.AddJobListener( new RockJobListener(), EverythingMatcher<JobKey>.AllJobs() ); // start the scheduler sched.Start(); }
/// <summary> /// Handles the Start event of the Application 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 Application_Start( object sender, EventArgs e ) { // Check if database should be auto-migrated bool autoMigrate = true; if ( !Boolean.TryParse( ConfigurationManager.AppSettings["AutoMigrateDatabase"], out autoMigrate ) ) { autoMigrate = true; } if ( autoMigrate ) { Database.SetInitializer( new MigrateDatabaseToLatestVersion<Rock.Data.RockContext, Rock.Migrations.Configuration>() ); } // Preload the commonly used objects LoadCacheObjects(); // setup and launch the jobs infrastructure if running under IIS bool runJobsInContext = Convert.ToBoolean( ConfigurationManager.AppSettings["RunJobsInIISContext"] ); if ( runJobsInContext ) { ISchedulerFactory sf; // create scheduler sf = new StdSchedulerFactory(); sched = sf.GetScheduler(); // get list of active jobs ServiceJobService jobService = new ServiceJobService(); foreach ( ServiceJob job in jobService.GetActiveJobs().ToList() ) { try { IJobDetail jobDetail = jobService.BuildQuartzJob( job ); ITrigger jobTrigger = jobService.BuildQuartzTrigger( job ); sched.ScheduleJob( jobDetail, jobTrigger ); } 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"; jobService.Save( job, null ); } } // set up the listener to report back from jobs as they complete sched.ListenerManager.AddJobListener( new RockJobListener(), EverythingMatcher<JobKey>.AllJobs() ); // start the scheduler sched.Start(); } // add call back to keep IIS process awake at night and to provide a timer for the queued transactions AddCallBack(); RegisterFilters( GlobalConfiguration.Configuration.Filters ); RegisterRoutes( RouteTable.Routes ); Rock.Security.Authorization.Load(); AddEventHandlers(); }
/// <summary> /// Updates the scheduled jobs. /// </summary> /// <param name="context">The context.</param> private void UpdateScheduledJobs( IJobExecutionContext context ) { var scheduler = context.Scheduler; int jobsDeleted = 0; int jobsScheduleUpdated = 0; var rockContext = new Rock.Data.RockContext(); ServiceJobService jobService = new ServiceJobService( rockContext ); List<ServiceJob> activeJobList = jobService.GetActiveJobs().ToList(); List<Quartz.JobKey> scheduledQuartzJobs = scheduler.GetJobKeys( GroupMatcher<JobKey>.GroupStartsWith( string.Empty ) ).ToList(); // delete any jobs that are no longer exist (are are not set to active) in the database var quartsJobsToDelete = scheduledQuartzJobs.Where( a => !activeJobList.Any( j => j.Guid.Equals( a.Name.AsGuid() ) ) ); foreach ( JobKey jobKey in quartsJobsToDelete ) { scheduler.DeleteJob( jobKey ); jobsDeleted++; } // add any jobs that are not yet scheduled var newActiveJobs = activeJobList.Where( a => !scheduledQuartzJobs.Any( q => q.Name.AsGuid().Equals( a.Guid ) ) ); foreach ( Rock.Model.ServiceJob job in newActiveJobs ) { const string errorSchedulingStatus = "Error scheduling Job"; try { IJobDetail jobDetail = jobService.BuildQuartzJob( job ); ITrigger jobTrigger = jobService.BuildQuartzTrigger( job ); scheduler.ScheduleJob( jobDetail, jobTrigger ); jobsScheduleUpdated++; if ( job.LastStatus == errorSchedulingStatus ) { job.LastStatusMessage = string.Empty; job.LastStatus = string.Empty; rockContext.SaveChanges(); } } catch ( Exception ex ) { ExceptionLogService.LogException( ex, null ); // create a friendly error message string message = string.Format( "Error scheduling the job: {0}.\n\n{2}", job.Name, job.Assembly, ex.Message ); job.LastStatusMessage = message; job.LastStatus = errorSchedulingStatus; } } rockContext.SaveChanges(); // reload the jobs in case any where added/removed scheduledQuartzJobs = scheduler.GetJobKeys( GroupMatcher<JobKey>.GroupStartsWith( string.Empty ) ).ToList(); // update schedule if the schedule has changed foreach ( var jobKey in scheduledQuartzJobs ) { var jobCronTrigger = scheduler.GetTriggersOfJob( jobKey ).OfType<ICronTrigger>().FirstOrDefault(); if ( jobCronTrigger != null ) { var activeJob = activeJobList.FirstOrDefault( a => a.Guid.Equals( jobKey.Name.AsGuid() ) ); if ( activeJob != null ) { bool rescheduleJob = false; // fix up the schedule if it has changed if ( activeJob.CronExpression != jobCronTrigger.CronExpressionString ) { rescheduleJob = true; } // update the job detail if it has changed var scheduledJobDetail = scheduler.GetJobDetail( jobKey ); var jobDetail = jobService.BuildQuartzJob( activeJob ); if ( scheduledJobDetail != null && jobDetail != null ) { if ( scheduledJobDetail.JobType != jobDetail.JobType ) { rescheduleJob = true; } if ( scheduledJobDetail.JobDataMap.ToJson() != jobDetail.JobDataMap.ToJson() ) { rescheduleJob = true; } } if ( rescheduleJob ) { const string errorReschedulingStatus = "Error re-scheduling Job"; try { ITrigger newJobTrigger = jobService.BuildQuartzTrigger( activeJob ); bool deletedSuccessfully = scheduler.DeleteJob( jobKey ); scheduler.ScheduleJob( jobDetail, newJobTrigger ); jobsScheduleUpdated++; if ( activeJob.LastStatus == errorReschedulingStatus ) { activeJob.LastStatusMessage = string.Empty; activeJob.LastStatus = string.Empty; rockContext.SaveChanges(); } } catch ( Exception ex ) { ExceptionLogService.LogException( ex, null ); // create a friendly error message string message = string.Format( "Error re-scheduling the job: {0}.\n\n{2}", activeJob.Name, activeJob.Assembly, ex.Message ); activeJob.LastStatusMessage = message; activeJob.LastStatus = errorReschedulingStatus; } } } } } context.Result = string.Empty; if ( jobsDeleted > 0 ) { context.Result += string.Format( "Deleted {0} job schedule(s)", jobsDeleted ); } if ( jobsScheduleUpdated > 0 ) { context.Result += ( string.IsNullOrEmpty( context.Result as string ) ? "" : " and " ) + string.Format( "Updated {0} schedule(s)", jobsScheduleUpdated ); } }
/// <summary> /// Handles the RunNow event of the gScheduledJobs 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_RunNow( object sender, RowEventArgs e ) { var job = new ServiceJobService( new RockContext() ).Get( e.RowKeyId ); if ( job != null ) { var transaction = new Rock.Transactions.RunJobNowTransaction( job.Id ); // Process the transaction on another thread System.Threading.Tasks.Task.Run( () => transaction.Execute() ); mdGridWarning.Show( string.Format( "The '{0}' job has been started.", job.Name ), ModalAlertType.Information ); // wait a split second for the job to start so that the grid will show the status (if it changed) System.Threading.Thread.Sleep( 250 ); } BindGrid(); }
/// <summary> /// Shows the detail. /// </summary> /// <param name="serviceJobId">The service job identifier.</param> public void ShowDetail( int serviceJobId ) { pnlDetails.Visible = true; LoadDropDowns(); // Load depending on Add(0) or Edit ServiceJob job = null; if ( !serviceJobId.Equals( 0 ) ) { job = new ServiceJobService( new RockContext() ).Get( serviceJobId ); lActionTitle.Text = ActionTitle.Edit( ServiceJob.FriendlyTypeName ).FormatAsHtmlTitle(); pdAuditDetails.SetEntity( job, ResolveRockUrl( "~" ) ); } if ( job == null ) { job = new ServiceJob { Id = 0, IsActive = true }; lActionTitle.Text = ActionTitle.Add( ServiceJob.FriendlyTypeName ).FormatAsHtmlTitle(); // hide the panel drawer that show created and last modified dates pdAuditDetails.Visible = false; } hfId.Value = job.Id.ToString(); tbName.Text = job.Name; tbDescription.Text = job.Description; cbActive.Checked = job.IsActive.HasValue ? job.IsActive.Value : false; ddlJobTypes.SelectedValue = job.Class; tbNotificationEmails.Text = job.NotificationEmails; ddlNotificationStatus.SetValue( (int)job.NotificationStatus ); tbCronExpression.Text = job.CronExpression; if (job.Id == 0) { job.Class = ddlJobTypes.SelectedValue; lCronExpressionDesc.Visible = false; lLastStatusMessage.Visible = false; } else { lCronExpressionDesc.Text = ExpressionDescriptor.GetDescription( job.CronExpression, new Options { ThrowExceptionOnParseError = false } ); lCronExpressionDesc.Visible = true; lLastStatusMessage.Text = job.LastStatusMessage.ConvertCrLfToHtmlBr(); lLastStatusMessage.Visible = true; } job.LoadAttributes(); phAttributes.Controls.Clear(); Rock.Attribute.Helper.AddEditControls( job, phAttributes, true, BlockValidationGroup ); // render UI based on Authorized and IsSystem bool readOnly = false; nbEditModeMessage.Text = string.Empty; if ( !IsUserAuthorized( Authorization.EDIT ) ) { readOnly = true; nbEditModeMessage.Text = EditModeMessage.ReadOnlyEditActionNotAllowed( ServiceJob.FriendlyTypeName ); } if ( job.IsSystem ) { readOnly = true; nbEditModeMessage.Text = EditModeMessage.ReadOnlySystem( ServiceJob.FriendlyTypeName ); } if ( readOnly ) { lActionTitle.Text = ActionTitle.View( ServiceJob.FriendlyTypeName ).FormatAsHtmlTitle(); btnCancel.Text = "Close"; Rock.Attribute.Helper.AddDisplayControls( job, phAttributesReadOnly ); phAttributesReadOnly.Visible = true; phAttributes.Visible = false; tbCronExpression.Text = job.CronExpression; } tbName.ReadOnly = readOnly; tbDescription.ReadOnly = readOnly; cbActive.Enabled = !readOnly; ddlJobTypes.Enabled = !readOnly; tbNotificationEmails.ReadOnly = readOnly; ddlNotificationStatus.Enabled = !readOnly; tbCronExpression.ReadOnly = readOnly; btnSave.Visible = !readOnly; }
/// <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> /// 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> /// Shows the detail. /// </summary> /// <param name="itemKey">The item key.</param> /// <param name="itemKeyValue">The item key value.</param> public void ShowDetail( string itemKey, int itemKeyValue ) { // return if unexpected itemKey if ( itemKey != "serviceJobId" ) { return; } pnlDetails.Visible = true; LoadDropDowns(); // Load depending on Add(0) or Edit ServiceJob job; if ( !itemKeyValue.Equals( 0 ) ) { job = new ServiceJobService( new RockContext() ).Get( itemKeyValue ); lActionTitle.Text = ActionTitle.Edit( ServiceJob.FriendlyTypeName ).FormatAsHtmlTitle(); } else { job = new ServiceJob { Id = 0, IsActive = true }; lActionTitle.Text = ActionTitle.Add( ServiceJob.FriendlyTypeName ).FormatAsHtmlTitle(); } hfId.Value = job.Id.ToString(); tbName.Text = job.Name; tbDescription.Text = job.Description; cbActive.Checked = job.IsActive.HasValue ? job.IsActive.Value : false; ddlJobTypes.SelectedValue = job.Class; tbNotificationEmails.Text = job.NotificationEmails; ddlNotificationStatus.SetValue( (int)job.NotificationStatus ); tbCronExpression.Text = job.CronExpression; if (job.Id == 0) { job.Class = ddlJobTypes.SelectedValue; lCronExpressionDesc.Visible = false; } else { lCronExpressionDesc.Text = ExpressionDescriptor.GetDescription( job.CronExpression ); lCronExpressionDesc.Visible = true; } job.LoadAttributes(); phAttributes.Controls.Clear(); Rock.Attribute.Helper.AddEditControls( job, phAttributes, true ); // render UI based on Authorized and IsSystem bool readOnly = false; nbEditModeMessage.Text = string.Empty; if ( !IsUserAuthorized( Authorization.EDIT ) ) { readOnly = true; nbEditModeMessage.Text = EditModeMessage.ReadOnlyEditActionNotAllowed( ServiceJob.FriendlyTypeName ); } if ( job.IsSystem ) { readOnly = true; nbEditModeMessage.Text = EditModeMessage.ReadOnlySystem( ServiceJob.FriendlyTypeName ); } if ( readOnly ) { lActionTitle.Text = ActionTitle.View( ServiceJob.FriendlyTypeName ).FormatAsHtmlTitle(); btnCancel.Text = "Close"; Rock.Attribute.Helper.AddDisplayControls( job, phAttributesReadOnly ); phAttributesReadOnly.Visible = true; phAttributes.Visible = false; tbCronExpression.Text = job.CronExpression; } tbName.ReadOnly = readOnly; tbDescription.ReadOnly = readOnly; cbActive.Enabled = !readOnly; ddlJobTypes.Enabled = !readOnly; tbNotificationEmails.ReadOnly = readOnly; ddlNotificationStatus.Enabled = !readOnly; tbCronExpression.ReadOnly = readOnly; btnSave.Visible = !readOnly; }
/// <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 } }
/// <summary> /// Handles the Start event of the Application 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 Application_Start( object sender, EventArgs e ) { try { DateTime startDateTime = RockDateTime.Now; if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { System.Diagnostics.Debug.WriteLine( string.Format( "Application_Start: {0}", RockDateTime.Now.ToString( "hh:mm:ss.FFF" ) ) ); } // Temporary code for v1.0.9 to delete payflowpro files in old location (The current update is not able to delete them, but 1.0.9 installs a fix for that) // This should be removed after 1.0.9 try { string physicalFile = System.Web.HttpContext.Current.Server.MapPath( @"~\Plugins\Payflow_dotNET.dll" ); if ( System.IO.File.Exists( physicalFile ) ) { System.IO.File.Delete( physicalFile ); } physicalFile = System.Web.HttpContext.Current.Server.MapPath( @"~\Plugins\Rock.PayFlowPro.dll" ); if ( System.IO.File.Exists( physicalFile ) ) { System.IO.File.Delete( physicalFile ); } } catch { // Intentionally Blank } // Get a db context var rockContext = new RockContext(); //// Run any needed Rock and/or plugin migrations //// NOTE: MigrateDatabase must be the first thing that touches the database to help prevent EF from creating empty tables for a new database MigrateDatabase( rockContext ); if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { try { new AttributeService( rockContext ).Get( 0 ); System.Diagnostics.Debug.WriteLine( string.Format( "ConnectToDatabase - {0} ms", ( RockDateTime.Now - startDateTime ).TotalMilliseconds ) ); startDateTime = RockDateTime.Now; } catch { // Intentionally Blank } } RegisterRoutes( rockContext, RouteTable.Routes ); // Preload the commonly used objects LoadCacheObjects( rockContext ); if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { System.Diagnostics.Debug.WriteLine( string.Format( "LoadCacheObjects - {0} ms", ( RockDateTime.Now - startDateTime ).TotalMilliseconds ) ); startDateTime = RockDateTime.Now; } // setup and launch the jobs infrastructure if running under IIS bool runJobsInContext = Convert.ToBoolean( ConfigurationManager.AppSettings["RunJobsInIISContext"] ); if ( runJobsInContext ) { ISchedulerFactory sf; // create scheduler sf = new StdSchedulerFactory(); sched = sf.GetScheduler(); // get list of active jobs ServiceJobService jobService = new ServiceJobService( rockContext ); foreach ( ServiceJob job in jobService.GetActiveJobs().ToList() ) { try { IJobDetail jobDetail = jobService.BuildQuartzJob( job ); ITrigger jobTrigger = jobService.BuildQuartzTrigger( job ); sched.ScheduleJob( jobDetail, jobTrigger ); } 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(); } } // set up the listener to report back from jobs as they complete sched.ListenerManager.AddJobListener( new RockJobListener(), EverythingMatcher<JobKey>.AllJobs() ); // start the scheduler sched.Start(); } // add call back to keep IIS process awake at night and to provide a timer for the queued transactions AddCallBack(); GlobalConfiguration.Configuration.EnableCors( new Rock.Rest.EnableCorsFromOriginAttribute() ); RegisterFilters( GlobalConfiguration.Configuration.Filters ); Rock.Security.Authorization.Load( rockContext ); EntityTypeService.RegisterEntityTypes( Server.MapPath( "~" ) ); FieldTypeService.RegisterFieldTypes( Server.MapPath( "~" ) ); BundleConfig.RegisterBundles( BundleTable.Bundles ); // mark any user login stored as 'IsOnline' in the database as offline MarkOnlineUsersOffline(); SqlServerTypes.Utilities.LoadNativeAssemblies( Server.MapPath( "~" ) ); } catch ( Exception ex ) { Error66( ex ); } }
/// <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 Start event of the Application 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 Application_Start( object sender, EventArgs e ) { if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { System.Diagnostics.Debug.WriteLine( string.Format( "Application_Start: {0}", DateTime.Now.ToString("hh:mm:ss.FFF" ) )); } // Check if database should be auto-migrated for the core and plugins bool autoMigrate = true; if ( !Boolean.TryParse( ConfigurationManager.AppSettings["AutoMigrateDatabase"], out autoMigrate ) ) { autoMigrate = true; } if ( autoMigrate ) { try { Database.SetInitializer( new MigrateDatabaseToLatestVersion<Rock.Data.RockContext, Rock.Migrations.Configuration>() ); // explictly check if the database exists, and force create it if doesn't exist Rock.Data.RockContext rockContext = new Rock.Data.RockContext(); if ( !rockContext.Database.Exists() ) { rockContext.Database.Initialize( true ); } else { var migrator = new System.Data.Entity.Migrations.DbMigrator( new Rock.Migrations.Configuration() ); migrator.Update(); } // Migrate any plugins that have pending migrations List<Type> configurationTypeList = Rock.Reflection.FindTypes( typeof( System.Data.Entity.Migrations.DbMigrationsConfiguration ) ).Select( a => a.Value ).ToList(); foreach ( var configType in configurationTypeList ) { if ( configType != typeof( Rock.Migrations.Configuration ) ) { var config = Activator.CreateInstance( configType ) as System.Data.Entity.Migrations.DbMigrationsConfiguration; System.Data.Entity.Migrations.DbMigrator pluginMigrator = Activator.CreateInstance( typeof( System.Data.Entity.Migrations.DbMigrator ), config ) as System.Data.Entity.Migrations.DbMigrator; pluginMigrator.Update(); } } } catch ( Exception ex ) { // if migrations fail, log error and attempt to continue LogError( ex, null ); } } else { // default Initializer is CreateDatabaseIfNotExists, but we don't want that to happen if automigrate is false, so set it to NULL so that nothing happens Database.SetInitializer<Rock.Data.RockContext>( null ); } if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { new AttributeService().Get( 0 ); System.Diagnostics.Debug.WriteLine( string.Format( "ConnectToDatabase - Connected: {0}", DateTime.Now.ToString( "hh:mm:ss.FFF" ) ) ); } // Preload the commonly used objects LoadCacheObjects(); if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { System.Diagnostics.Debug.WriteLine( string.Format( "LoadCacheObjects - Done: {0}", DateTime.Now.ToString( "hh:mm:ss.FFF" ) ) ); } // setup and launch the jobs infrastructure if running under IIS bool runJobsInContext = Convert.ToBoolean( ConfigurationManager.AppSettings["RunJobsInIISContext"] ); if ( runJobsInContext ) { ISchedulerFactory sf; // create scheduler sf = new StdSchedulerFactory(); sched = sf.GetScheduler(); // get list of active jobs ServiceJobService jobService = new ServiceJobService(); foreach ( ServiceJob job in jobService.GetActiveJobs().ToList() ) { try { IJobDetail jobDetail = jobService.BuildQuartzJob( job ); ITrigger jobTrigger = jobService.BuildQuartzTrigger( job ); sched.ScheduleJob( jobDetail, jobTrigger ); } 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"; jobService.Save( job, null ); } } // set up the listener to report back from jobs as they complete sched.ListenerManager.AddJobListener( new RockJobListener(), EverythingMatcher<JobKey>.AllJobs() ); // start the scheduler sched.Start(); } // add call back to keep IIS process awake at night and to provide a timer for the queued transactions AddCallBack(); RegisterFilters( GlobalConfiguration.Configuration.Filters ); RegisterRoutes( RouteTable.Routes ); Rock.Security.Authorization.Load(); AddEventHandlers(); new EntityTypeService().RegisterEntityTypes( Server.MapPath( "~" ) ); new FieldTypeService().RegisterFieldTypes( Server.MapPath( "~" ) ); BundleConfig.RegisterBundles( BundleTable.Bundles ); }
/// <summary> /// Handles the SelectedIndexChanged event of the ddlJobTypes 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 ddlJobTypes_SelectedIndexChanged( object sender, EventArgs e ) { ServiceJob job; var itemId = PageParameter( "serviceJobId" ).AsInteger(); if ( itemId == 0 ) { job = new ServiceJob { Id = 0, IsActive = true }; } else { job = new ServiceJobService( new RockContext() ).Get( itemId ); } job.Class = ddlJobTypes.SelectedValue; job.LoadAttributes(); phAttributes.Controls.Clear(); Rock.Attribute.Helper.AddEditControls( job, phAttributes, true, BlockValidationGroup ); }
/// <summary> /// Handles the Start event of the Application 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 Application_Start( object sender, EventArgs e ) { try { if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { System.Diagnostics.Debug.WriteLine( string.Format( "Application_Start: {0}", RockDateTime.Now.ToString( "hh:mm:ss.FFF" ) ) ); } // Run any needed Rock and/or plugin migrations MigrateDatabase(); // Get a db context var rockContext = new RockContext(); RegisterRoutes( rockContext, RouteTable.Routes ); if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { new AttributeService( rockContext ).Get( 0 ); System.Diagnostics.Debug.WriteLine( string.Format( "ConnectToDatabase - Connected: {0}", RockDateTime.Now.ToString( "hh:mm:ss.FFF" ) ) ); } // Preload the commonly used objects LoadCacheObjects( rockContext ); if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { System.Diagnostics.Debug.WriteLine( string.Format( "LoadCacheObjects - Done: {0}", RockDateTime.Now.ToString( "hh:mm:ss.FFF" ) ) ); } // setup and launch the jobs infrastructure if running under IIS bool runJobsInContext = Convert.ToBoolean( ConfigurationManager.AppSettings["RunJobsInIISContext"] ); if ( runJobsInContext ) { ISchedulerFactory sf; // create scheduler sf = new StdSchedulerFactory(); sched = sf.GetScheduler(); // get list of active jobs ServiceJobService jobService = new ServiceJobService( rockContext ); foreach ( ServiceJob job in jobService.GetActiveJobs().ToList() ) { try { IJobDetail jobDetail = jobService.BuildQuartzJob( job ); ITrigger jobTrigger = jobService.BuildQuartzTrigger( job ); sched.ScheduleJob( jobDetail, jobTrigger ); } 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(); } } // set up the listener to report back from jobs as they complete sched.ListenerManager.AddJobListener( new RockJobListener(), EverythingMatcher<JobKey>.AllJobs() ); // start the scheduler sched.Start(); } // add call back to keep IIS process awake at night and to provide a timer for the queued transactions AddCallBack(); RegisterFilters( GlobalConfiguration.Configuration.Filters ); Rock.Security.Authorization.Load( rockContext ); EntityTypeService.RegisterEntityTypes( Server.MapPath( "~" ) ); FieldTypeService.RegisterFieldTypes( Server.MapPath( "~" ) ); BundleConfig.RegisterBundles( BundleTable.Bundles ); // mark any user login stored as 'IsOnline' in the database as offline MarkOnlineUsersOffline(); SqlServerTypes.Utilities.LoadNativeAssemblies( Server.MapPath( "~" ) ); } catch ( Exception ex ) { Error66( ex ); } }
/// <summary> /// Handles the Start event of the Application 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 Application_Start( object sender, EventArgs e ) { try { var stopwatch = System.Diagnostics.Stopwatch.StartNew(); LogMessage( APP_LOG_FILENAME, "Application Starting..." ); if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { System.Diagnostics.Debug.WriteLine( string.Format( "Application_Start: {0}", RockDateTime.Now.ToString( "hh:mm:ss.FFF" ) ) ); } // Clear all cache RockMemoryCache.Clear(); // If not migrating, set up view cache to speed up startup (Not supported when running migrations). var fileInfo = new FileInfo( Server.MapPath( "~/App_Data/Run.Migration" ) ); if ( !fileInfo.Exists ) { RockInteractiveViews.SetViewFactory( Server.MapPath( "~/App_Data/RockModelViews.xml" ) ); } // Get a db context using ( var rockContext = new RockContext() ) { if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { try { // default Initializer is CreateDatabaseIfNotExists, so set it to NULL so that nothing happens if there isn't a database yet Database.SetInitializer<Rock.Data.RockContext>( null ); new AttributeService( rockContext ).Get( 0 ); System.Diagnostics.Debug.WriteLine( string.Format( "ConnectToDatabase {2}/{1} - {0} ms", stopwatch.Elapsed.TotalMilliseconds, rockContext.Database.Connection.Database, rockContext.Database.Connection.DataSource ) ); } catch { // Intentionally Blank } } //// Run any needed Rock and/or plugin migrations //// NOTE: MigrateDatabase must be the first thing that touches the database to help prevent EF from creating empty tables for a new database MigrateDatabase( rockContext ); // Preload the commonly used objects stopwatch.Restart(); LoadCacheObjects( rockContext ); if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { System.Diagnostics.Debug.WriteLine( string.Format( "LoadCacheObjects - {0} ms", stopwatch.Elapsed.TotalMilliseconds ) ); } // Run any plugin migrations MigratePlugins( rockContext ); RegisterRoutes( rockContext, RouteTable.Routes ); // Configure Rock Rest API stopwatch.Restart(); GlobalConfiguration.Configure( Rock.Rest.WebApiConfig.Register ); if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { System.Diagnostics.Debug.WriteLine( string.Format( "Configure WebApiConfig - {0} ms", stopwatch.Elapsed.TotalMilliseconds ) ); stopwatch.Restart(); } // setup and launch the jobs infrastructure if running under IIS bool runJobsInContext = Convert.ToBoolean( ConfigurationManager.AppSettings["RunJobsInIISContext"] ); if ( runJobsInContext ) { ISchedulerFactory sf; // create scheduler sf = new StdSchedulerFactory(); sched = sf.GetScheduler(); // get list of active jobs ServiceJobService jobService = new ServiceJobService( rockContext ); foreach ( ServiceJob job in jobService.GetActiveJobs().ToList() ) { const string errorLoadingStatus = "Error Loading Job"; try { IJobDetail jobDetail = jobService.BuildQuartzJob( job ); ITrigger jobTrigger = jobService.BuildQuartzTrigger( job ); sched.ScheduleJob( jobDetail, jobTrigger ); //// if the last status was an error, but we now loaded successful, clear the error // also, if the last status was 'Running', clear that status because it would have stopped if the app restarted if ( job.LastStatus == errorLoadingStatus || job.LastStatus == "Running" ) { job.LastStatusMessage = string.Empty; job.LastStatus = string.Empty; rockContext.SaveChanges(); } } catch ( Exception ex ) { // log the error LogError( ex, null ); // create a friendly error message string message = string.Format( "Error loading the job: {0}.\n\n{2}", job.Name, job.Assembly, ex.Message ); job.LastStatusMessage = message; job.LastStatus = errorLoadingStatus; rockContext.SaveChanges(); } } // set up the listener to report back from jobs as they complete sched.ListenerManager.AddJobListener( new RockJobListener(), EverythingMatcher<JobKey>.AllJobs() ); // start the scheduler sched.Start(); } // set the encryption protocols that are permissible for external SSL connections System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls | System.Net.SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls12; // Force the static Liquid class to get instantiated so that the standard filters are loaded prior // to the custom RockFilter. This is to allow the custom 'Date' filter to replace the standard // Date filter. Liquid.UseRubyDateFormat = false; //// NOTE: This means that template filters will also use CSharpNamingConvention //// For example the dotliquid documentation says to do this for formatting dates: //// {{ some_date_value | date:"MMM dd, yyyy" }} //// However, if CSharpNamingConvention is enabled, it needs to be: //// {{ some_date_value | Date:"MMM dd, yyyy" }} Template.NamingConvention = new DotLiquid.NamingConventions.CSharpNamingConvention(); Template.FileSystem = new RockWeb.LavaFileSystem(); Template.RegisterSafeType( typeof( Enum ), o => o.ToString() ); Template.RegisterFilter( typeof( Rock.Lava.RockFilters ) ); // add call back to keep IIS process awake at night and to provide a timer for the queued transactions AddCallBack(); Rock.Security.Authorization.Load(); } EntityTypeService.RegisterEntityTypes( Server.MapPath( "~" ) ); FieldTypeService.RegisterFieldTypes( Server.MapPath( "~" ) ); BundleConfig.RegisterBundles( BundleTable.Bundles ); // mark any user login stored as 'IsOnline' in the database as offline MarkOnlineUsersOffline(); SqlServerTypes.Utilities.LoadNativeAssemblies( Server.MapPath( "~" ) ); LogMessage( APP_LOG_FILENAME, "Application Started Successfully" ); if ( System.Web.Hosting.HostingEnvironment.IsDevelopmentEnvironment ) { System.Diagnostics.Debug.WriteLine( string.Format( "Application_Started_Successfully: {0}", RockDateTime.Now.ToString( "hh:mm:ss.FFF" ) ) ); } } catch (Exception ex) { SetError66(); throw ( new Exception( "Error occurred during application startup", ex ) ); } // Update attributes for new workflow actions new Thread( () => { Rock.Workflow.ActionContainer.Instance.UpdateAttributes(); } ).Start(); // compile less files new Thread( () => { Thread.CurrentThread.IsBackground = true; RockTheme.CompileAll(); } ).Start(); }
/// <summary> /// Schedules the Job to run immediately using the Quartz Scheduler /// and waits for the job to finish. /// Returns <c>false</c> with an <c>out</c> <paramref name="errorMessage"/> if the job is already running as a RunNow job or if an exception occurs. /// NOTE: This will take at least 10 seconds to ensure the Quartz scheduler successfully started the job, plus any additional time that might /// still be needed to complete the job. /// </summary> /// <param name="job">The job.</param> /// <param name="errorMessage">The error message.</param> /// <returns></returns> public bool RunNow(ServiceJob job, out string errorMessage) { // use a new RockContext instead of using this.Context so we can SaveChanges without affecting other RockContext's with pending changes. var rockContext = new RockContext(); errorMessage = string.Empty; try { // create a scheduler specific for the job var scheduleConfig = new NameValueCollection(); var jobId = job.Id; 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 errorMessage = "Job already running as a RunNow job"; return(false); } // Check if another scheduler is running this job try { var otherSchedulers = new StdSchedulerFactory() .AllSchedulers .Where(s => s.SchedulerName != runNowSchedulerName); foreach (var scheduler in otherSchedulers) { var isAlreadyRunning = scheduler.GetCurrentlyExecutingJobs() .Where(j => j.JobDetail.Description == jobId.ToString() && j.JobDetail.ConcurrentExectionDisallowed) .Any(); if (isAlreadyRunning) { // A job with that Id is already running and ConcurrentExectionDisallowed is true errorMessage = $" Scheduler '{scheduler.SchedulerName}' is already executing job Id '{jobId}' (name: {job.Name})"; System.Diagnostics.Debug.WriteLine($"{RockDateTime.Now.ToString()} {errorMessage}"); return(false); } } } catch { // Was blank in the RunJobNowTransaction (intentional?) } // create the quartz job and trigger var jobDetail = new ServiceJobService(rockContext).BuildQuartzJob(job); var jobDataMap = jobDetail.JobDataMap; if (jobDataMap != null) { // Force the <string, string> dictionary so that within Jobs, it is always okay to use // JobDataMap.GetString(). This mimics Rock attributes always being stored as strings. // If we allow non-strings, like integers, then JobDataMap.GetString() throws an exception. jobDetail.JobDataMap.PutAll(jobDataMap.ToDictionary(kvp => kvp.Key, kvp => ( object )kvp.Value)); } 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 scheduler to start the job. // If we don't do this, the scheduler might Shutdown thinking there are no running jobs Task.Delay(10 * 1000).Wait(); // stop the scheduler when done with job sched.Shutdown(true); return(true); } catch (Exception ex) { // create a friendly error message ExceptionLogService.LogException(ex, null); errorMessage = string.Format("Error doing a 'Run Now' on job: {0}. \n\n{2}", job.Name, job.Assembly, ex.Message); job.LastStatusMessage = errorMessage; job.LastStatus = "Error Loading Job"; rockContext.SaveChanges(); var jobHistoryService = new ServiceJobHistoryService(rockContext); var jobHistory = new ServiceJobHistory { ServiceJobId = job.Id, StartDateTime = RockDateTime.Now, StopDateTime = RockDateTime.Now, Status = job.LastStatus, StatusMessage = job.LastStatusMessage }; jobHistoryService.Add(jobHistory); rockContext.SaveChanges(); return(false); } }
/// <summary> /// Binds the scheduled jobs. /// </summary> private void BindGrid() { var jobService = new ServiceJobService(); SortProperty sortProperty = gScheduledJobs.SortProperty; if ( sortProperty != null ) { gScheduledJobs.DataSource = jobService.GetAllJobs().Sort( sortProperty ).ToList(); } else { gScheduledJobs.DataSource = jobService.GetAllJobs().OrderByDescending( a => a.LastRunDateTime ).ToList(); } gScheduledJobs.DataBind(); }
/// <summary> /// Handles the RunNow event of the gScheduledJobs 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_RunNow( object sender, RowEventArgs e ) { var job = new ServiceJobService( new RockContext() ).Get( e.RowKeyId ); if ( job != null ) { var transaction = new Rock.Transactions.RunJobNowTransaction( job.Id ); Rock.Transactions.RockQueue.TransactionQueue.Enqueue( transaction ); mdGridWarning.Show( string.Format( "The '{0}' job has been triggered to run and will start within the next two minutes ( during the next transaction cycle ).", job.Name ), ModalAlertType.Information ); } BindGrid(); }
/// <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> /// Starts the job scheduler. /// </summary> public void StartJobScheduler() { if ( !System.IO.File.Exists( "web.connectionstrings.config" ) ) { // Write an eventlog about web.connectionstring.config not found this.EventLog.WriteEntry( "Unable to find web.connectionstrings.config", EventLogEntryType.Error ); } ISchedulerFactory sf; // create scheduler sf = new StdSchedulerFactory(); sched = sf.GetScheduler(); var rockContext = new RockContext(); // get list of active jobs ServiceJobService jobService = new ServiceJobService( rockContext ); List<ServiceJob> activeJobs = null; try { // make sure that we can connect to the database and get the jobs list. Write a good EventLog message and exit the app if we can't this.EventLog.WriteEntry( string.Format( "Connecting to database {0}:{1}", rockContext.Database.Connection.DataSource, rockContext.Database.Connection.Database ), EventLogEntryType.Information ); rockContext.Database.Connection.Open(); activeJobs = jobService.GetActiveJobs().ToList(); } catch ( Exception ex ) { this.EventLog.WriteEntry( "Unable load active jobs list. " + ex.Message, EventLogEntryType.Error ); throw ex; } foreach ( ServiceJob job in activeJobs ) { try { IJobDetail jobDetail = jobService.BuildQuartzJob( job ); ITrigger jobTrigger = jobService.BuildQuartzTrigger( job ); sched.ScheduleJob( jobDetail, jobTrigger ); } catch ( Exception ex ) { // get path to the services directory string path = System.Reflection.Assembly.GetExecutingAssembly().Location; path = System.IO.Path.GetDirectoryName( path ); // 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 services directory ({2}) of your server.", job.Name, job.Assembly, path ); message = message + "\n\n\n\n" + ex.Message; //throw new JobLoadFailedException( message ); job.LastStatusMessage = message; job.LastStatus = "Error Loading Job"; rockContext.SaveChanges(); } } // set up the listener to report back from jobs as they complete sched.ListenerManager.AddJobListener( new RockJobListener(), EverythingMatcher<JobKey>.AllJobs() ); // start the scheduler sched.Start(); }