Base solution for your next web application
Open Closed

Quartz configuration from NameValueCollection #9968


User avatar
0
ivanosw1 created

We need to configure Quartz to use SQL storage, but we can not store the connection string in the static quartz.config file. In the AbpQuartzModule.PreInitialize() the AbpQuartzConfiguration is registred to resolve from the StdSchedulerFactory.GetDefaultScheduler(), but we would need to register our own implementation of IAbpQuartzConfiguration to create a scheduler with our configuration.

    class AbpCustomQuartzConfiguration : IAbpQuartzConfiguration
    {
        public IScheduler Scheduler
        {
            get
            {

                var builder = SchedulerBuilder.Create();
                builder.UsePersistentStore(options =>
                {
                    options.UseClustering();
                    options.UseSqlServer(sqlServerOptions =>
                    {
                        sqlServerOptions.ConnectionString = "...";
                    });
                    options.UseSerializer<JsonObjectSerializer>();
                });
                builder.SchedulerId = StdSchedulerFactory.AutoGenerateInstanceId;
                builder.MisfireThreshold = TimeSpan.FromMinutes(5);

                var scheduler = builder.BuildScheduler().Result;

                scheduler.JobFactory = new AbpQuartzJobFactory(IocManager.Instance);
                scheduler.ListenerManager.AddJobListener(IocManager.Instance.Resolve<IJobListener>());

                return scheduler;
            }
        }
    }

How can we accomplish this?

  • Version: Abp 5.14
  • Framework: .NET Core

6 Answer(s)
  • User Avatar
    0
    zony created
    Support Team

    Hi ivanosw1 You can use Configuration.ReplaceService<TInterface,TImplemention> to replace the registration. e.g.

        public class YourModule : AbpModule
        {
            public override void PreInitialize()
            {
                Configuration.ReplaceService<IAbpQuartzConfiguration, AbpCustomQuartzConfiguration>();
            }
        }
    
  • User Avatar
    0
    ivanosw1 created

    Hi Zony, if I use the ReplaceService the registred type is still overwritten by Abp.Quartz

    and thus the IScheduler is not properly configred. Am I missing something?

    Furhermore, the Abp.Quartz.PreInitialize calls indirectly the StdSchedulerFactory.GetDefaultScheduler(), resulting in the creation of a default in-memory sheduler even before my custom PreInitialize gets called. In this way I will end up with 2 different scheduler instances.

  • User Avatar
    0
    zony created
    Support Team

    Hi ivanosw1, Please try the code:

    IocManager.IocContainer.Register(Component.For<IAbpQuartzConfiguration, QuartzStartupConfiguration>().IsDefault());
    

  • User Avatar
    0
    ivanosw1 created

    Hi, this registration does work, but from the logs I can confirm the suspect that 2 different schedulers are initialized:

    ... 
    INFO  2021-01-08 09:33:15,969 [1    ] Quartz.Impl.StdSchedulerFactory          - Default Quartz.NET properties loaded from embedded resource file
    INFO  2021-01-08 09:33:16,021 [1    ] Quartz.Core.SchedulerSignalerImpl        - Initialized Scheduler Signaller of type: Quartz.Core.SchedulerSignalerImpl
    INFO  2021-01-08 09:33:16,027 [1    ] Quartz.Core.QuartzScheduler              - Quartz Scheduler v.3.0.7.0 created.
    INFO  2021-01-08 09:33:16,028 [1    ] Quartz.Simpl.RAMJobStore                 - RAMJobStore initialized.
    INFO  2021-01-08 09:33:16,030 [1    ] Quartz.Core.QuartzScheduler              - Scheduler meta-data: Quartz Scheduler (v3.0.7.0) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED'
      Scheduler class: 'Quartz.Core.QuartzScheduler' - running locally.
      NOT STARTED.
      Currently in standby mode.
      Number of jobs executed: 0
      Using thread pool 'Quartz.Simpl.DefaultThreadPool' - with 10 threads.
      Using job-store 'Quartz.Simpl.RAMJobStore' - which does not support persistence. and is not clustered.
    
    INFO  2021-01-08 09:33:16,034 [1    ] Quartz.Impl.StdSchedulerFactory          - Quartz scheduler 'DefaultQuartzScheduler' initialized
    INFO  2021-01-08 09:33:16,034 [1    ] Quartz.Impl.StdSchedulerFactory          - Quartz scheduler version: 3.0.7.0
    INFO  2021-01-08 09:33:16,034 [1    ] Quartz.Core.QuartzScheduler              - JobFactory set to: Abp.Quartz.AbpQuartzJobFactory
    WARN  2021-01-08 09:33:21,647 [1    ] tion.Repositories.EphemeralXmlRepository - Using an in-memory repository. Keys will not be persisted to storage.
    WARN  2021-01-08 09:33:21,648 [1    ] taProtection.KeyManagement.XmlKeyManager - Neither user profile nor HKLM registry available. Using an ephemeral key repository. Protected data will be unavailable when application exits.
    INFO  2021-01-08 09:33:24,436 [1    ] Quartz.Util.DBConnectionManager          - Registering datasource 'UnoInsDataSource' with db provider: 'Quartz.Impl.AdoJobStore.Common.DbProvider'
    INFO  2021-01-08 09:33:24,440 [1    ] Quartz.Impl.StdSchedulerFactory          - Using object serializer: Quartz.Simpl.JsonObjectSerializer, Quartz.Serialization.Json
    INFO  2021-01-08 09:33:25,304 [9    ] Quartz.Core.SchedulerSignalerImpl        - Initialized Scheduler Signaller of type: Quartz.Core.SchedulerSignalerImpl
    INFO  2021-01-08 09:33:25,305 [9    ] Quartz.Core.QuartzScheduler              - Quartz Scheduler v.3.0.7.0 created.
    INFO  2021-01-08 09:33:25,311 [9    ] Quartz.Impl.AdoJobStore.JobStoreTX       - Detected usage of SqlServerDelegate - defaulting 'selectWithLockSQL' to 'SELECT * FROM QRTZ_LOCKS WITH (UPDLOCK,ROWLOCK) WHERE SCHED_NAME = 'Framework Scheduler' AND LOCK_NAME = @lockName'.
    INFO  2021-01-08 09:33:25,311 [9    ] Quartz.Impl.AdoJobStore.JobStoreTX       - Using db table-based data access locking (synchronization).
    INFO  2021-01-08 09:33:25,314 [9    ] Quartz.Impl.AdoJobStore.JobStoreTX       - JobStoreTX initialized.
    INFO  2021-01-08 09:33:25,314 [9    ] Quartz.Core.QuartzScheduler              - Scheduler meta-data: Quartz Scheduler (v3.0.7.0) 'Framework Scheduler' with instanceId 'DEV-N00015.637456916052992244'
      Scheduler class: 'Quartz.Core.QuartzScheduler' - running locally.
      NOT STARTED.
      Currently in standby mode.
      Number of jobs executed: 0
      Using thread pool 'Quartz.Simpl.DefaultThreadPool' - with 10 threads.
      Using job-store 'Quartz.Impl.AdoJobStore.JobStoreTX' - which supports persistence. and is clustered.
      ...
    

    The Abp.Quartz indeed creates a default scheduler regardless of what I do in my PreInitialize

        public class AbpQuartzModule : AbpModule
        {
            public override void PreInitialize()
            {
                IocManager.Register<IAbpQuartzConfiguration, AbpQuartzConfiguration>();
        
                // this line creates the default in-memory scheduler even if i replace the IAbpQuartzConfiguration in my PreInitialize
                Configuration.Modules.AbpQuartz().Scheduler.JobFactory = new AbpQuartzJobFactory(IocManager);
            }
    

    For the moment I am going to shut the default one down before replacing it with my own.

  • User Avatar
    0
    ivanosw1 created

    This seems to be a cleaner solution

    [DependsOn(typeof(AbpQuartzModule))]
    public class MyModule : AbpModule
    {
    
        public MyModule()
        {
            // patch configuration before PreInitialize. 
            // Do not use IocManager property in the constructor as it is not populated 
            Abp.Dependency.IocManager.Instance.IocContainer.Register(
                Component
                    .For<IAbpQuartzConfiguration, QuartzStartupConfiguration>()
                    .LifestyleSingleton()
                    .IsDefault());
        }
    
        // ..
    }
    
  • User Avatar
    0
    ismcagdas created
    Support Team

    Hi @ivanosw1,

    Thank you for sharing this :)