mirror of https://github.com/abpframework/abp.git
16 changed files with 302 additions and 124 deletions
@ -0,0 +1,119 @@ |
|||||
|
using System; |
||||
|
using System.Diagnostics; |
||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Microsoft.Extensions.Logging; |
||||
|
using Microsoft.Extensions.Logging.Abstractions; |
||||
|
using Microsoft.Extensions.Options; |
||||
|
using Volo.Abp.DependencyInjection; |
||||
|
using Volo.Abp.Threading; |
||||
|
using Volo.Abp.Timing; |
||||
|
|
||||
|
namespace Volo.Abp.BackgroundJobs |
||||
|
{ |
||||
|
public class BackgroundJobExecuter : IBackgroundJobExecuter, ITransientDependency |
||||
|
{ |
||||
|
public ILogger<BackgroundJobExecuter> Logger { protected get; set; } |
||||
|
|
||||
|
protected IServiceProvider ServiceProvider { get; } |
||||
|
protected IClock Clock { get; } |
||||
|
protected IBackgroundJobSerializer Serializer { get; } |
||||
|
protected IBackgroundJobStore Store { get; } |
||||
|
protected BackgroundJobOptions Options { get; } |
||||
|
|
||||
|
public BackgroundJobExecuter( |
||||
|
IServiceProvider serviceProvider, |
||||
|
IClock clock, |
||||
|
IBackgroundJobSerializer serializer, |
||||
|
IBackgroundJobStore store, |
||||
|
IOptions<BackgroundJobOptions> options) |
||||
|
{ |
||||
|
ServiceProvider = serviceProvider; |
||||
|
Clock = clock; |
||||
|
Serializer = serializer; |
||||
|
Options = options.Value; |
||||
|
Store = store; |
||||
|
|
||||
|
Logger = NullLogger<BackgroundJobExecuter>.Instance; |
||||
|
} |
||||
|
|
||||
|
public void Execute(BackgroundJobInfo jobInfo) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
jobInfo.TryCount++; |
||||
|
jobInfo.LastTryTime = Clock.Now; |
||||
|
|
||||
|
var jobType = Options.GetJobType(jobInfo.JobName); |
||||
|
|
||||
|
using (var scope = ServiceProvider.CreateScope()) |
||||
|
{ |
||||
|
var job = scope.ServiceProvider.GetService(jobType); |
||||
|
if (job == null) |
||||
|
{ |
||||
|
throw new AbpException("JobName is not registered: " + jobType); |
||||
|
} |
||||
|
|
||||
|
//TODO: Type check for the job object
|
||||
|
|
||||
|
var jobExecuteMethod = job.GetType().GetMethod("Execute"); |
||||
|
Debug.Assert(jobExecuteMethod != null, nameof(jobExecuteMethod) + " != null"); |
||||
|
var argsType = jobExecuteMethod.GetParameters()[0].ParameterType; |
||||
|
var argsObj = Serializer.Deserialize(jobInfo.JobArgs, argsType); |
||||
|
|
||||
|
try |
||||
|
{ |
||||
|
jobExecuteMethod.Invoke(job, new[] { argsObj }); |
||||
|
AsyncHelper.RunSync(() => Store.DeleteAsync(jobInfo)); |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
Logger.LogException(ex); |
||||
|
|
||||
|
var nextTryTime = jobInfo.CalculateNextTryTime(Clock); |
||||
|
if (nextTryTime.HasValue) |
||||
|
{ |
||||
|
jobInfo.NextTryTime = nextTryTime.Value; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
jobInfo.IsAbandoned = true; |
||||
|
} |
||||
|
|
||||
|
TryUpdate(jobInfo); |
||||
|
|
||||
|
var backgroundJobException = new BackgroundJobException( |
||||
|
"A background job execution is failed. See inner exception for details. See BackgroundJob property to get information on the background job.", |
||||
|
ex |
||||
|
) |
||||
|
{ |
||||
|
BackgroundJob = jobInfo, |
||||
|
JobObject = job |
||||
|
}; |
||||
|
|
||||
|
//TODO: Somehow trigger an event for the exception (may create an Volo.Abp.ExceptionHandling package)!
|
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
catch (Exception ex) |
||||
|
{ |
||||
|
Logger.LogException(ex); |
||||
|
|
||||
|
jobInfo.IsAbandoned = true; |
||||
|
|
||||
|
TryUpdate(jobInfo); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void TryUpdate(BackgroundJobInfo jobInfo) |
||||
|
{ |
||||
|
try |
||||
|
{ |
||||
|
Store.UpdateAsync(jobInfo); |
||||
|
} |
||||
|
catch (Exception updateEx) |
||||
|
{ |
||||
|
Logger.LogException(updateEx); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,30 @@ |
|||||
|
using System; |
||||
|
using System.Linq; |
||||
|
using JetBrains.Annotations; |
||||
|
|
||||
|
namespace Volo.Abp.BackgroundJobs |
||||
|
{ |
||||
|
public class BackgroundJobNameAttribute : Attribute, IBackgroundJobNameProvider |
||||
|
{ |
||||
|
public string Name { get; } |
||||
|
|
||||
|
public BackgroundJobNameAttribute([NotNull] string name) |
||||
|
{ |
||||
|
Name = Check.NotNullOrWhiteSpace(name, nameof(name)); |
||||
|
} |
||||
|
|
||||
|
public static string GetNameOrNull<TJobArgs>() |
||||
|
{ |
||||
|
return GetNameOrNull(typeof(TJobArgs)); |
||||
|
} |
||||
|
|
||||
|
public static string GetNameOrNull(Type jobArgsType) |
||||
|
{ |
||||
|
return jobArgsType |
||||
|
.GetCustomAttributes(true) |
||||
|
.OfType<IBackgroundJobNameProvider>() |
||||
|
.FirstOrDefault() |
||||
|
?.Name; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,12 +1,29 @@ |
|||||
namespace Volo.Abp.BackgroundJobs |
using System; |
||||
|
using System.Collections.Generic; |
||||
|
|
||||
|
namespace Volo.Abp.BackgroundJobs |
||||
{ |
{ |
||||
public class BackgroundJobOptions |
public class BackgroundJobOptions |
||||
{ |
{ |
||||
|
public Dictionary<string, Type> JobTypes { get; } |
||||
|
|
||||
public bool IsJobExecutionEnabled { get; set; } |
public bool IsJobExecutionEnabled { get; set; } |
||||
|
|
||||
public BackgroundJobOptions() |
public BackgroundJobOptions() |
||||
{ |
{ |
||||
IsJobExecutionEnabled = true; |
IsJobExecutionEnabled = true; |
||||
|
JobTypes = new Dictionary<string, Type>(); |
||||
|
} |
||||
|
|
||||
|
public Type GetJobType(string jobName) |
||||
|
{ |
||||
|
var jobType = JobTypes.GetOrDefault(jobName); |
||||
|
if (jobType == null) |
||||
|
{ |
||||
|
throw new AbpException("Undefined background job type for the job name: " + jobName); |
||||
|
} |
||||
|
|
||||
|
return jobType; |
||||
} |
} |
||||
} |
} |
||||
} |
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Volo.Abp.BackgroundJobs |
||||
|
{ |
||||
|
public interface IBackgroundJobExecuter |
||||
|
{ |
||||
|
void Execute(BackgroundJobInfo jobInfo); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Volo.Abp.BackgroundJobs |
||||
|
{ |
||||
|
public interface IBackgroundJobNameProvider |
||||
|
{ |
||||
|
string Name { get; } |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,2 @@ |
|||||
|
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> |
||||
|
<s:String x:Key="/Default/CodeInspection/CSharpLanguageProject/LanguageLevel/@EntryValue">CSharp71</s:String></wpf:ResourceDictionary> |
||||
@ -0,0 +1,16 @@ |
|||||
|
using Microsoft.Extensions.DependencyInjection; |
||||
|
using Volo.Abp.Modularity; |
||||
|
|
||||
|
namespace Volo.Abp.BackgroundJobs |
||||
|
{ |
||||
|
[DependsOn( |
||||
|
typeof(AbpBackgroundJobsModule) |
||||
|
)] |
||||
|
public class AbpBackgroundJobsTestModule : AbpModule |
||||
|
{ |
||||
|
public override void ConfigureServices(ServiceConfigurationContext context) |
||||
|
{ |
||||
|
context.Services.AddAssemblyOf<AbpBackgroundJobsTestModule>(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,42 @@ |
|||||
|
using System.Threading.Tasks; |
||||
|
using Shouldly; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.BackgroundJobs |
||||
|
{ |
||||
|
public class BackgroundJobManager_Tests : BackgroundJobsTestBase |
||||
|
{ |
||||
|
private readonly IBackgroundJobManager _backgroundJobManager; |
||||
|
private readonly IBackgroundJobStore _backgroundJobStore; |
||||
|
|
||||
|
public BackgroundJobManager_Tests() |
||||
|
{ |
||||
|
_backgroundJobManager = GetRequiredService<IBackgroundJobManager>(); |
||||
|
_backgroundJobStore = GetRequiredService<IBackgroundJobStore>(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public async Task Should_Store_Jobs() |
||||
|
{ |
||||
|
var jobId = await _backgroundJobManager.EnqueueAsync(new MyJobArgs("42")); |
||||
|
jobId.ShouldNotBe(default); |
||||
|
(await _backgroundJobStore.FindAsync(jobId)).ShouldNotBeNull(); |
||||
|
} |
||||
|
|
||||
|
[BackgroundJobName("TestJobs.MyJob")] |
||||
|
private class MyJobArgs |
||||
|
{ |
||||
|
public string Value { get; set; } |
||||
|
|
||||
|
public MyJobArgs() |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
|
||||
|
public MyJobArgs(string value) |
||||
|
{ |
||||
|
Value = value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,7 @@ |
|||||
|
namespace Volo.Abp.BackgroundJobs |
||||
|
{ |
||||
|
public abstract class BackgroundJobsTestBase : AbpIntegratedTest<AbpBackgroundJobsTestModule> |
||||
|
{ |
||||
|
|
||||
|
} |
||||
|
} |
||||
@ -1,7 +0,0 @@ |
|||||
namespace Volo.Abp.BackgroundJobs |
|
||||
{ |
|
||||
public class Class1 |
|
||||
{ |
|
||||
|
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue