Merge pull request #1902 from sbwalker/dev
improve Scheduled Job start/stop user experience, utilize start time when setting next job execution
This commit is contained in:
commit
ea93ab2a83
@ -36,13 +36,13 @@ else
|
|||||||
<td>@context.NextExecution</td>
|
<td>@context.NextExecution</td>
|
||||||
<td>
|
<td>
|
||||||
@if (context.IsStarted)
|
@if (context.IsStarted)
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-danger" @onclick="(async () => await StopJob(context.JobId))">@Localizer["Stop"]</button>
|
<button type="button" class="btn btn-danger" @onclick="(async () => await StopJob(context.JobId))">@Localizer["Stop"]</button>
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
<button type="button" class="btn btn-success" @onclick="(async () => await StartJob(context.JobId))">@Localizer["Start"]</button>
|
<button type="button" class="btn btn-success" @onclick="(async () => await StartJob(context.JobId))">@Localizer["Start"]</button>
|
||||||
}
|
}
|
||||||
</td>
|
</td>
|
||||||
</Row>
|
</Row>
|
||||||
</Pager>
|
</Pager>
|
||||||
@ -100,11 +100,6 @@ else
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (interval > 1)
|
|
||||||
{
|
|
||||||
result += Localizer["s"];
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -114,6 +109,7 @@ else
|
|||||||
{
|
{
|
||||||
await JobService.DeleteJobAsync(job.JobId);
|
await JobService.DeleteJobAsync(job.JobId);
|
||||||
await logger.LogInformation("Job Deleted {Job}", job);
|
await logger.LogInformation("Job Deleted {Job}", job);
|
||||||
|
_jobs = await JobService.GetJobsAsync();
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -125,12 +121,36 @@ else
|
|||||||
|
|
||||||
private async Task StartJob(int jobId)
|
private async Task StartJob(int jobId)
|
||||||
{
|
{
|
||||||
await JobService.StartJobAsync(jobId);
|
try
|
||||||
|
{
|
||||||
|
await JobService.StartJobAsync(jobId);
|
||||||
|
await logger.LogInformation("Job Started {JobId}", jobId);
|
||||||
|
AddModuleMessage(Localizer["Message.Job.Start"], MessageType.Success);
|
||||||
|
_jobs = await JobService.GetJobsAsync();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Starting Job {JobId} {Error}", jobId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Job.Start"], MessageType.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task StopJob(int jobId)
|
private async Task StopJob(int jobId)
|
||||||
{
|
{
|
||||||
await JobService.StopJobAsync(jobId);
|
try
|
||||||
|
{
|
||||||
|
await JobService.StopJobAsync(jobId);
|
||||||
|
await logger.LogInformation("Job Stopped {JobId}", jobId);
|
||||||
|
AddModuleMessage(Localizer["Message.Job.Stop"], MessageType.Success);
|
||||||
|
_jobs = await JobService.GetJobsAsync();
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await logger.LogError(ex, "Error Stopping Job {JobId} {Error}", jobId, ex.Message);
|
||||||
|
AddModuleMessage(Localizer["Error.Job.Stop"], MessageType.Error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task Refresh()
|
private async Task Refresh()
|
||||||
|
@ -144,9 +144,6 @@
|
|||||||
<data name="Month" xml:space="preserve">
|
<data name="Month" xml:space="preserve">
|
||||||
<value>Month</value>
|
<value>Month</value>
|
||||||
</data>
|
</data>
|
||||||
<data name="s" xml:space="preserve">
|
|
||||||
<value>s</value>
|
|
||||||
</data>
|
|
||||||
<data name="Error.Job.Delete" xml:space="preserve">
|
<data name="Error.Job.Delete" xml:space="preserve">
|
||||||
<value>Error Deleting Job</value>
|
<value>Error Deleting Job</value>
|
||||||
</data>
|
</data>
|
||||||
@ -177,4 +174,16 @@
|
|||||||
<data name="JobLog.Text" xml:space="preserve">
|
<data name="JobLog.Text" xml:space="preserve">
|
||||||
<value>Log</value>
|
<value>Log</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="Error.Job.Start" xml:space="preserve">
|
||||||
|
<value>An error occurred when starting the job</value>
|
||||||
|
</data>
|
||||||
|
<data name="Error.Job.Stop" xml:space="preserve">
|
||||||
|
<value>An error occurred when stopping the job</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.Job.Start" xml:space="preserve">
|
||||||
|
<value>The process responsible for executing this job has been started. The next execution will be based on the schedule criteria for the job.</value>
|
||||||
|
</data>
|
||||||
|
<data name="Message.Job.Stop" xml:space="preserve">
|
||||||
|
<value>The process responsible for executing this job has been stopped. In order to restart the process you will need to use the Start button or restart the application.</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
@ -114,7 +114,7 @@ namespace Oqtane.Infrastructure
|
|||||||
jobLogs.UpdateJobLog(log);
|
jobLogs.UpdateJobLog(log);
|
||||||
|
|
||||||
// update the job
|
// update the job
|
||||||
job.NextExecution = CalculateNextExecution(NextExecution, job.Frequency, job.Interval);
|
job.NextExecution = CalculateNextExecution(NextExecution, job);
|
||||||
job.IsExecuting = false;
|
job.IsExecuting = false;
|
||||||
jobs.UpdateJob(job);
|
jobs.UpdateJob(job);
|
||||||
|
|
||||||
@ -140,21 +140,31 @@ namespace Oqtane.Infrastructure
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private DateTime CalculateNextExecution(DateTime nextExecution, string frequency, int interval)
|
private DateTime CalculateNextExecution(DateTime nextExecution, Job job)
|
||||||
{
|
{
|
||||||
switch (frequency)
|
switch (job.Frequency)
|
||||||
{
|
{
|
||||||
case "m": // minutes
|
case "m": // minutes
|
||||||
nextExecution = nextExecution.AddMinutes(interval);
|
nextExecution = nextExecution.AddMinutes(job.Interval);
|
||||||
break;
|
break;
|
||||||
case "H": // hours
|
case "H": // hours
|
||||||
nextExecution = nextExecution.AddHours(interval);
|
nextExecution = nextExecution.AddHours(job.Interval);
|
||||||
break;
|
break;
|
||||||
case "d": // days
|
case "d": // days
|
||||||
nextExecution = nextExecution.AddDays(interval);
|
nextExecution = nextExecution.AddDays(job.Interval);
|
||||||
|
if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0)
|
||||||
|
{
|
||||||
|
// set the start time
|
||||||
|
nextExecution = nextExecution.Date.Add(job.StartDate.Value.TimeOfDay);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case "M": // months
|
case "M": // months
|
||||||
nextExecution = nextExecution.AddMonths(interval);
|
nextExecution = nextExecution.AddMonths(job.Interval);
|
||||||
|
if (job.StartDate != null && job.StartDate.Value.TimeOfDay.TotalSeconds != 0)
|
||||||
|
{
|
||||||
|
// set the start time
|
||||||
|
nextExecution = nextExecution.Date.Add(job.StartDate.Value.TimeOfDay);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (nextExecution < DateTime.UtcNow)
|
if (nextExecution < DateTime.UtcNow)
|
||||||
@ -168,7 +178,6 @@ namespace Oqtane.Infrastructure
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// set IsExecuting to false in case this job was forcefully terminated previously
|
|
||||||
using (var scope = _serviceScopeFactory.CreateScope())
|
using (var scope = _serviceScopeFactory.CreateScope())
|
||||||
{
|
{
|
||||||
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
||||||
@ -176,15 +185,17 @@ namespace Oqtane.Infrastructure
|
|||||||
Job job = jobs.GetJobs().Where(item => item.JobType == jobTypeName).FirstOrDefault();
|
Job job = jobs.GetJobs().Where(item => item.JobType == jobTypeName).FirstOrDefault();
|
||||||
if (job != null)
|
if (job != null)
|
||||||
{
|
{
|
||||||
|
// reset in case this job was forcefully terminated previously
|
||||||
job.IsStarted = true;
|
job.IsStarted = true;
|
||||||
job.IsExecuting = false;
|
job.IsExecuting = false;
|
||||||
jobs.UpdateJob(job);
|
jobs.UpdateJob(job);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// auto registration - does not run on initial installation but will run after restart
|
// auto registration - job will not run on initial installation but will run after restart
|
||||||
job = new Job { JobType = jobTypeName };
|
job = new Job { JobType = jobTypeName };
|
||||||
// optional properties
|
|
||||||
|
// optional HostedServiceBase properties
|
||||||
var jobType = Type.GetType(jobTypeName);
|
var jobType = Type.GetType(jobTypeName);
|
||||||
var jobObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, jobType) as HostedServiceBase;
|
var jobObject = ActivatorUtilities.CreateInstance(scope.ServiceProvider, jobType) as HostedServiceBase;
|
||||||
if (jobObject.Name != "")
|
if (jobObject.Name != "")
|
||||||
@ -203,6 +214,7 @@ namespace Oqtane.Infrastructure
|
|||||||
job.IsEnabled = jobObject.IsEnabled;
|
job.IsEnabled = jobObject.IsEnabled;
|
||||||
job.IsStarted = true;
|
job.IsStarted = true;
|
||||||
job.IsExecuting = false;
|
job.IsExecuting = false;
|
||||||
|
job.NextExecution = null;
|
||||||
jobs.AddJob(job);
|
jobs.AddJob(job);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -224,13 +236,27 @@ namespace Oqtane.Infrastructure
|
|||||||
|
|
||||||
public async Task StopAsync(CancellationToken cancellationToken)
|
public async Task StopAsync(CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
if (_executingTask == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
using (var scope = _serviceScopeFactory.CreateScope())
|
||||||
|
{
|
||||||
|
string jobTypeName = Utilities.GetFullTypeName(GetType().AssemblyQualifiedName);
|
||||||
|
IJobRepository jobs = scope.ServiceProvider.GetRequiredService<IJobRepository>();
|
||||||
|
Job job = jobs.GetJobs().Where(item => item.JobType == jobTypeName).FirstOrDefault();
|
||||||
|
if (job != null)
|
||||||
|
{
|
||||||
|
// reset job
|
||||||
|
job.IsStarted = false;
|
||||||
|
job.IsExecuting = false;
|
||||||
|
jobs.UpdateJob(job);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_executingTask == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_cancellationTokenSource.Cancel();
|
_cancellationTokenSource.Cancel();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
Reference in New Issue
Block a user