Quartz.Net Xml配置说明

XMLSchedulingDataProcessor 源码

/*
 * Copyright 2001-2010 Terracotta, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy
 * of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 *
 */

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;

using Quartz.Impl.Matchers;
using Quartz.Logging;
using Quartz.Spi;
using Quartz.Util;
using Quartz.Xml.JobSchedulingData20;

namespace Quartz.Xml
{
    /// <summary>
    /// Parses an XML file that declares Jobs and their schedules (Triggers).
    /// </summary>
    /// <remarks>
    /// <para>
    /// The xml document must conform to the format defined in "job_scheduling_data_2_0.xsd"
    /// </para>
    ///
    /// <para>
    /// After creating an instance of this class, you should call one of the <see cref="ProcessFile(CancellationToken)" />
    /// functions, after which you may call the ScheduledJobs()
    /// function to get a handle to the defined Jobs and Triggers, which can then be
    /// scheduled with the <see cref="IScheduler" />. Alternatively, you could call
    /// the <see cref="ProcessFileAndScheduleJobs(Quartz.IScheduler, CancellationToken)" /> function to do all of this
    /// in one step.
    /// </para>
    ///
    /// <para>
    /// The same instance can be used again and again, with the list of defined Jobs
    /// being cleared each time you call a <see cref="ProcessFile(CancellationToken)" /> method,
    /// however a single instance is not thread-safe.
    /// </para>
    /// </remarks>
    /// <author><a href="mailto:bonhamcm@thirdeyeconsulting.com">Chris Bonham</a></author>
    /// <author>James House</author>
    /// <author>Marko Lahma (.NET)</author>
    /// <author>Christian Krumm (.NET Bugfix)</author>
    public class XMLSchedulingDataProcessor
    {
        public const string QuartzXmlFileName = "quartz_jobs.xml";
        public const string QuartzXsdResourceName = "Quartz.Xml.job_scheduling_data_2_0.xsd";

        // pre-processing commands
        private readonly List<string> jobGroupsToDelete = new List<string>();
        private readonly List<string> triggerGroupsToDelete = new List<string>();
        private readonly List<JobKey> jobsToDelete = new List<JobKey>();
        private readonly List<TriggerKey> triggersToDelete = new List<TriggerKey>();

        // scheduling commands
        private readonly List<IJobDetail> loadedJobs = new List<IJobDetail>();
        private readonly List<ITrigger> loadedTriggers = new List<ITrigger>();

        // directives
        private readonly List<Exception> validationExceptions = new List<Exception>();

        private readonly List<string> jobGroupsToNeverDelete = new List<string>();
        private readonly List<string> triggerGroupsToNeverDelete = new List<string>();

        /// <summary>
        /// Constructor for XMLSchedulingDataProcessor.
        /// </summary>
        public XMLSchedulingDataProcessor(ITypeLoadHelper typeLoadHelper)
        {
            OverWriteExistingData = true;
            IgnoreDuplicates = false;
            Log = LogProvider.GetLogger(GetType());
            TypeLoadHelper = typeLoadHelper;
        }

        /// <summary>
        /// Whether the existing scheduling data (with same identifiers) will be
        /// overwritten.
        /// </summary>
        /// <remarks>
        /// If false, and <see cref="IgnoreDuplicates" /> is not false, and jobs or
        /// triggers with the same names already exist as those in the file, an
        /// error will occur.
        /// </remarks>
        /// <seealso cref="IgnoreDuplicates" />
        public virtual bool OverWriteExistingData { get; set; }

        /// <summary>
        /// If true (and <see cref="OverWriteExistingData" /> is false) then any
        /// job/triggers encountered in this file that have names that already exist
        /// in the scheduler will be ignored, and no error will be produced.
        /// </summary>
        /// <seealso cref="OverWriteExistingData"/>
        public virtual bool IgnoreDuplicates { get; set; }

        /// <summary>
        /// If true (and <see cref="OverWriteExistingData" /> is true) then any
        /// job/triggers encountered in this file that already exist is scheduler
        /// will be updated with start time relative to old trigger. Effectively
        /// new trigger's last fire time will be updated to old trigger's last fire time
        /// and trigger's next fire time will updated to be next from this last fire time.
        /// </summary>
        public virtual bool ScheduleTriggerRelativeToReplacedTrigger { get; set; }

        /// <summary>
        /// Gets the log.
        /// </summary>
        /// <value>The log.</value>
        private ILog Log { get; }

        protected virtual IReadOnlyList<IJobDetail> LoadedJobs => loadedJobs.AsReadOnly();

        protected virtual IReadOnlyList<ITrigger> LoadedTriggers => loadedTriggers.AsReadOnly();

        protected ITypeLoadHelper TypeLoadHelper { get; }

        /// <summary>
        /// Process the xml file in the default location (a file named
        /// "quartz_jobs.xml" in the current working directory).
        /// </summary>
        /// <param name="cancellationToken">The cancellation instruction.</param>
        public virtual Task ProcessFile(CancellationToken cancellationToken = default)
        {
            return ProcessFile(QuartzXmlFileName, cancellationToken);
        }

        /// <summary>
        /// Process the xml file named <see param="fileName" />.
        /// </summary>
        /// <param name="fileName">meta data file name.</param>
        /// <param name="cancellationToken">The cancellation instruction.</param>
        public virtual Task ProcessFile(
            string fileName,
            CancellationToken cancellationToken = default)
        {
            return ProcessFile(fileName, fileName, cancellationToken);
        }

        /// <summary>
        /// Process the xmlfile named <see param="fileName" /> with the given system
        /// ID.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="systemId">The system id.</param>
        /// <param name="cancellationToken">The cancellation instruction.</param>
        public virtual async Task ProcessFile(
            string fileName,
            string systemId,
            CancellationToken cancellationToken = default)
        {
            // resolve file name first
            fileName = FileUtil.ResolveFile(fileName) ?? fileName;

            Log.InfoFormat("Parsing XML file: {0} with systemId: {1}", fileName, systemId);

            using (var stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.Read))
            using (StreamReader sr = new StreamReader(stream))
            {
                ProcessInternal(await sr.ReadToEndAsync().ConfigureAwait(false));
            }
        }

        /// <summary>
        /// Process the xmlfile named <see param="fileName" /> with the given system
        /// ID.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <param name="systemId">The system id.</param>
        /// <param name="cancellationToken">The cancellation instruction.</param>
        public virtual async Task ProcessStream(
            Stream stream,
            string? systemId,
            CancellationToken cancellationToken = default)
        {
            Log.InfoFormat("Parsing XML from stream with systemId: {0}", systemId);
            using StreamReader sr = new StreamReader(stream);
            ProcessInternal(await sr.ReadToEndAsync().ConfigureAwait(false));
        }

        protected virtual void PrepForProcessing()
        {
            ClearValidationExceptions();

            OverWriteExistingData = true;
            IgnoreDuplicates = false;

            jobGroupsToDelete.Clear();
            jobsToDelete.Clear();
            triggerGroupsToDelete.Clear();
            triggersToDelete.Clear();

            loadedJobs.Clear();
            loadedTriggers.Clear();
        }

        protected virtual void ProcessInternal(string xml)
        {
            PrepForProcessing();

            ValidateXml(xml);
            MaybeThrowValidationException();

            // deserialize as object model
            var xs = new XmlSerializer(typeof (QuartzXmlConfiguration20));
            var data = (QuartzXmlConfiguration20?) xs.Deserialize(new StringReader(xml));

            if (data == null)
            {
                throw new SchedulerConfigException("Job definition data from XML was null after deserialization");
            }

            //
            // Extract pre-processing commands
            //
            if (data.preprocessingcommands != null)
            {
                foreach (preprocessingcommandsType command in data.preprocessingcommands)
                {
                    if (command.deletejobsingroup != null)
                    {
                        foreach (string s in command.deletejobsingroup)
                        {
                            var deleteJobGroup = s.NullSafeTrim();
                            if (!string.IsNullOrEmpty(deleteJobGroup))
                            {
                                jobGroupsToDelete.Add(deleteJobGroup);
                            }
                        }
                    }
                    if (command.deletetriggersingroup != null)
                    {
                        foreach (string s in command.deletetriggersingroup)
                        {
                            var deleteTriggerGroup = s.NullSafeTrim();
                            if (!string.IsNullOrEmpty(deleteTriggerGroup))
                            {
                                triggerGroupsToDelete.Add(deleteTriggerGroup);
                            }
                        }
                    }
                    if (command.deletejob != null)
                    {
                        foreach (preprocessingcommandsTypeDeletejob s in command.deletejob)
                        {
                            var name = s.name.TrimEmptyToNull();
                            var group = s.group.TrimEmptyToNull();

                            if (name == null)
                            {
                                throw new SchedulerConfigException("Encountered a 'delete-job' command without a name specified.");
                            }
                            jobsToDelete.Add(new JobKey(name, group!));
                        }
                    }
                    if (command.deletetrigger != null)
                    {
                        foreach (preprocessingcommandsTypeDeletetrigger s in command.deletetrigger)
                        {
                            var name = s.name.TrimEmptyToNull();
                            var group = s.group.TrimEmptyToNull();

                            if (name == null)
                            {
                                throw new SchedulerConfigException("Encountered a 'delete-trigger' command without a name specified.");
                            }
                            triggersToDelete.Add(new TriggerKey(name, group!));
                        }
                    }
                }
            }

            if (Log.IsDebugEnabled())
            {
                Log.Debug("Found " + jobGroupsToDelete.Count + " delete job group commands.");
                Log.Debug("Found " + triggerGroupsToDelete.Count + " delete trigger group commands.");
                Log.Debug("Found " + jobsToDelete.Count + " delete job commands.");
                Log.Debug("Found " + triggersToDelete.Count + " delete trigger commands.");
            }

            //
            // Extract directives
            //
            if (data.processingdirectives != null && data.processingdirectives.Length > 0)
            {
                bool overWrite = data.processingdirectives[0].overwriteexistingdata;
                Log.Debug("Directive 'overwrite-existing-data' specified as: " + overWrite);
                OverWriteExistingData = overWrite;
            }
            else
            {
                Log.Debug("Directive 'overwrite-existing-data' not specified, defaulting to " + OverWriteExistingData);
            }

            if (data.processingdirectives != null && data.processingdirectives.Length > 0)
            {
                bool ignoreduplicates = data.processingdirectives[0].ignoreduplicates;
                Log.Debug("Directive 'ignore-duplicates' specified as: " + ignoreduplicates);
                IgnoreDuplicates = ignoreduplicates;
            }
            else
            {
                Log.Debug("Directive 'ignore-duplicates' not specified, defaulting to " + IgnoreDuplicates);
            }

            if (data.processingdirectives != null && data.processingdirectives.Length > 0)
            {
                bool scheduleRelative = data.processingdirectives[0].scheduletriggerrelativetoreplacedtrigger;
                Log.Debug("Directive 'schedule-trigger-relative-to-replaced-trigger' specified as: " + scheduleRelative);
                ScheduleTriggerRelativeToReplacedTrigger = scheduleRelative;
            }
            else
            {
                Log.Debug("Directive 'schedule-trigger-relative-to-replaced-trigger' not specified, defaulting to " + ScheduleTriggerRelativeToReplacedTrigger);
            }

            //
            // Extract Job definitions...
            //
            List<jobdetailType> jobNodes = new List<jobdetailType>();
            if (data.schedule != null)
            {
                foreach (var schedule in data.schedule)
                {
                    if (schedule?.job != null)
                    {
                        jobNodes.AddRange(schedule.job);
                    }
                }
            }

            Log.Debug("Found " + jobNodes.Count + " job definitions.");

            foreach (jobdetailType jobDetailType in jobNodes)
            {
                var jobName = jobDetailType.name.TrimEmptyToNull();
                var jobGroup = jobDetailType.group.TrimEmptyToNull();
                var jobDescription = jobDetailType.description.TrimEmptyToNull();
                var jobTypeName = jobDetailType.jobtype.TrimEmptyToNull();
                bool jobDurability = jobDetailType.durable;
                bool jobRecoveryRequested = jobDetailType.recover;

                Type jobType = TypeLoadHelper.LoadType(jobTypeName!)!;

                IJobDetail jobDetail = JobBuilder.Create(jobType!)
                    .WithIdentity(jobName!, jobGroup!)
                    .WithDescription(jobDescription)
                    .StoreDurably(jobDurability)
                    .RequestRecovery(jobRecoveryRequested)
                    .Build();

                if (jobDetailType.jobdatamap != null && jobDetailType.jobdatamap.entry != null)
                {
                    foreach (entryType entry in jobDetailType.jobdatamap.entry)
                    {
                        var key = entry.key.Trim();
                        var value = entry.value.TrimEmptyToNull();
                        jobDetail.JobDataMap.Add(key, value!);
                    }
                }

                if (Log.IsDebugEnabled())
                {
                    Log.Debug("Parsed job definition: " + jobDetail);
                }

                AddJobToSchedule(jobDetail);
            }

            //
            // Extract Trigger definitions...
            //

            List<triggerType> triggerEntries = new List<triggerType>();
            if (data.schedule != null)
            {
                foreach (var schedule in data.schedule)
                {
                    if (schedule != null && schedule.trigger != null)
                    {
                        triggerEntries.AddRange(schedule.trigger);
                    }
                }
            }

            Log.Debug("Found " + triggerEntries.Count + " trigger definitions.");

            foreach (triggerType triggerNode in triggerEntries)
            {
                var triggerName = triggerNode.Item.name.TrimEmptyToNull()!;
                var triggerGroup = triggerNode.Item.group.TrimEmptyToNull()!;
                var triggerDescription = triggerNode.Item.description.TrimEmptyToNull();
                var triggerCalendarRef = triggerNode.Item.calendarname.TrimEmptyToNull();
                string triggerJobName = triggerNode.Item.jobname.TrimEmptyToNull()!;
                string triggerJobGroup = triggerNode.Item.jobgroup.TrimEmptyToNull()!;

                int triggerPriority = TriggerConstants.DefaultPriority;
                if (!triggerNode.Item.priority.IsNullOrWhiteSpace())
                {
                    triggerPriority = Convert.ToInt32(triggerNode.Item.priority);
                }

                DateTimeOffset triggerStartTime = SystemTime.UtcNow();
                if (triggerNode.Item.Item != null)
                {
                    if (triggerNode.Item.Item is DateTime time)
                    {
                        triggerStartTime = new DateTimeOffset(time);
                    }
                    else
                    {
                        triggerStartTime = triggerStartTime.AddSeconds(Convert.ToInt32(triggerNode.Item.Item));
                    }
                }

                DateTime? triggerEndTime = triggerNode.Item.endtimeSpecified ? triggerNode.Item.endtime : (DateTime?) null;

                IScheduleBuilder sched;

                if (triggerNode.Item is simpleTriggerType simpleTrigger)
                {
                    var repeatCountString = simpleTrigger.repeatcount.TrimEmptyToNull();
                    var repeatIntervalString = simpleTrigger.repeatinterval.TrimEmptyToNull();

                    int repeatCount = ParseSimpleTriggerRepeatCount(repeatCountString!);
                    TimeSpan repeatInterval = repeatIntervalString == null ? TimeSpan.Zero : TimeSpan.FromMilliseconds(Convert.ToInt64(repeatIntervalString));

                    sched = SimpleScheduleBuilder.Create()
                        .WithInterval(repeatInterval)
                        .WithRepeatCount(repeatCount);

                    if (!simpleTrigger.misfireinstruction.IsNullOrWhiteSpace())
                    {
                        ((SimpleScheduleBuilder) sched).WithMisfireHandlingInstruction(ReadMisfireInstructionFromString(simpleTrigger.misfireinstruction));
                    }
                }
                else if (triggerNode.Item is cronTriggerType)
                {
                    cronTriggerType cronTrigger = (cronTriggerType) triggerNode.Item;
                    var cronExpression = cronTrigger.cronexpression.TrimEmptyToNull();
                    var timezoneString = cronTrigger.timezone.TrimEmptyToNull();

                    TimeZoneInfo? tz = timezoneString != null ? TimeZoneUtil.FindTimeZoneById(timezoneString) : null;
                    sched = CronScheduleBuilder.CronSchedule(cronExpression!)
                        .InTimeZone(tz!);

                    if (!cronTrigger.misfireinstruction.IsNullOrWhiteSpace())
                    {
                        ((CronScheduleBuilder) sched).WithMisfireHandlingInstruction(ReadMisfireInstructionFromString(cronTrigger.misfireinstruction));
                    }
                }
                else if (triggerNode.Item is calendarIntervalTriggerType)
                {
                    calendarIntervalTriggerType calendarIntervalTrigger = (calendarIntervalTriggerType) triggerNode.Item;
                    var repeatIntervalString = calendarIntervalTrigger.repeatinterval.TrimEmptyToNull();

                    IntervalUnit intervalUnit = ParseDateIntervalTriggerIntervalUnit(calendarIntervalTrigger.repeatintervalunit.TrimEmptyToNull());
                    int repeatInterval = repeatIntervalString == null ? 0 : Convert.ToInt32(repeatIntervalString);

                    sched = CalendarIntervalScheduleBuilder.Create()
                        .WithInterval(repeatInterval, intervalUnit);

                    if (!calendarIntervalTrigger.misfireinstruction.IsNullOrWhiteSpace())
                    {
                        ((CalendarIntervalScheduleBuilder) sched).WithMisfireHandlingInstruction(ReadMisfireInstructionFromString(calendarIntervalTrigger.misfireinstruction));
                    }
                }
                else
                {
                    throw new SchedulerConfigException("Unknown trigger type in XML configuration");
                }

                IMutableTrigger trigger = (IMutableTrigger) TriggerBuilder.Create()
                                                                .WithIdentity(triggerName, triggerGroup)
                                                                .WithDescription(triggerDescription)
                                                                .ForJob(triggerJobName, triggerJobGroup)
                                                                .StartAt(triggerStartTime)
                                                                .EndAt(triggerEndTime)
                                                                .WithPriority(triggerPriority)
                                                                .ModifiedByCalendar(triggerCalendarRef)
                                                                .WithSchedule(sched)
                                                                .Build();

                if (triggerNode.Item.jobdatamap != null && triggerNode.Item.jobdatamap.entry != null)
                {
                    foreach (entryType entry in triggerNode.Item.jobdatamap.entry)
                    {
                        string key = entry.key.TrimEmptyToNull()!;
                        var value = entry.value.TrimEmptyToNull();
                        trigger.JobDataMap.Add(key, value!);
                    }
                }

                if (Log.IsDebugEnabled())
                {
                    Log.Debug("Parsed trigger definition: " + trigger);
                }

                AddTriggerToSchedule(trigger);
            }
        }

        protected virtual void AddJobToSchedule(IJobDetail job)
        {
            loadedJobs.Add(job);
        }

        protected virtual void AddTriggerToSchedule(IMutableTrigger trigger)
        {
            loadedTriggers.Add(trigger);
        }

        protected virtual int ParseSimpleTriggerRepeatCount(string repeatcount)
        {
            int value = Convert.ToInt32(repeatcount, CultureInfo.InvariantCulture);
            return value;
        }

        protected virtual int ReadMisfireInstructionFromString(string misfireinstruction)
        {
            Constants c = new Constants(typeof (MisfireInstruction), typeof (MisfireInstruction.CronTrigger),
                                        typeof (MisfireInstruction.SimpleTrigger));
            return c.AsNumber(misfireinstruction);
        }

        protected virtual IntervalUnit ParseDateIntervalTriggerIntervalUnit(string? intervalUnit)
        {
            if (string.IsNullOrEmpty(intervalUnit))
            {
                return IntervalUnit.Day;
            }

            if (!TryParseEnum(intervalUnit, out IntervalUnit retValue))
            {
                throw new SchedulerConfigException("Unknown interval unit for DateIntervalTrigger: " + intervalUnit);
            }
            return retValue;
        }

        protected virtual bool TryParseEnum<T>(string str, out T value) where T : struct
        {
            var names = Enum.GetNames(typeof (T));
            value = (T) Enum.GetValues(typeof (T)).GetValue(0)!;
            foreach (var name in names)
            {
                if (name == str)
                {
                    value = (T) Enum.Parse(typeof (T), name);
                    return true;
                }
            }
            return false;
        }

        private void ValidateXml(string xml)
        {
            try
            {
                var settings = new XmlReaderSettings
                {
                    ValidationType = ValidationType.Schema, 
                    ValidationFlags = XmlSchemaValidationFlags.ProcessInlineSchema 
                                      | XmlSchemaValidationFlags.ProcessSchemaLocation 
                                      | XmlSchemaValidationFlags.ReportValidationWarnings
                };

                using var stream = typeof(XMLSchedulingDataProcessor).Assembly.GetManifestResourceStream(QuartzXsdResourceName);

                if (stream is null)
                {
                    throw new Exception("Could not read XSD from embedded resource");
                }
                
                var schema = XmlSchema.Read(stream, XmlValidationCallBack);
                settings.Schemas.Add(schema!);
                settings.ValidationEventHandler += XmlValidationCallBack;

                // stream to validate
                using var reader = XmlReader.Create(new StringReader(xml), settings);
                while (reader.Read())
                {
                }
            }
            catch (Exception ex)
            {
                Log.WarnException("Unable to validate XML with schema: " + ex.Message, ex);
            }
        }

        private void XmlValidationCallBack(object? sender, ValidationEventArgs e)
        {
            if (e.Severity == XmlSeverityType.Error)
            {
                validationExceptions.Add(e.Exception);
            }
            else
            {
                Log.Warn(e.Message);
            }
        }

        /// <summary>
        /// Process the xml file in the default location, and schedule all of the jobs defined within it.
        /// </summary>
        /// <remarks>Note that we will set overWriteExistingJobs after the default xml is parsed.</remarks>
        public async Task ProcessFileAndScheduleJobs(
            IScheduler sched,
            bool overWriteExistingJobs,
            CancellationToken cancellationToken = default)
        {
            await ProcessFile(QuartzXmlFileName, QuartzXmlFileName, cancellationToken).ConfigureAwait(false);
            // The overWriteExistingJobs flag was set by processFile() -> prepForProcessing(), then by xml parsing, and then now
            // we need to reset it again here by this method parameter to override it.
            OverWriteExistingData = overWriteExistingJobs;
            await ExecutePreProcessCommands(sched, cancellationToken).ConfigureAwait(false);
            await ScheduleJobs(sched, cancellationToken).ConfigureAwait(false);
        }

        /// <summary>
        /// Process the xml file in the default location, and schedule all of the
        /// jobs defined within it.
        /// </summary>
        public virtual Task ProcessFileAndScheduleJobs(
            IScheduler sched,
            CancellationToken cancellationToken = default)
        {
            return ProcessFileAndScheduleJobs(QuartzXmlFileName, sched, cancellationToken);
        }

        /// <summary>
        /// Process the xml file in the given location, and schedule all of the
        /// jobs defined within it.
        /// </summary>
        /// <param name="fileName">meta data file name.</param>
        /// <param name="sched">The scheduler.</param>
        /// <param name="cancellationToken">The cancellation instruction.</param>
        public virtual Task ProcessFileAndScheduleJobs(
            string fileName,
            IScheduler sched,
            CancellationToken cancellationToken = default)
        {
            return ProcessFileAndScheduleJobs(fileName, fileName, sched, cancellationToken);
        }

        /// <summary>
        /// Process the xml file in the given location, and schedule all of the
        /// jobs defined within it.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="systemId">The system id.</param>
        /// <param name="sched">The sched.</param>
        /// <param name="cancellationToken">The cancellation instruction.</param>
        public virtual async Task ProcessFileAndScheduleJobs(
            string fileName,
            string systemId,
            IScheduler sched,
            CancellationToken cancellationToken = default)
        {
            await ProcessFile(fileName, systemId, cancellationToken).ConfigureAwait(false);
            await ExecutePreProcessCommands(sched, cancellationToken).ConfigureAwait(false);
            await ScheduleJobs(sched, cancellationToken).ConfigureAwait(false);
        }

        /// <summary>
        /// Process the xml file in the given location, and schedule all of the
        /// jobs defined within it.
        /// </summary>
        /// <param name="stream">stream to read XML data from.</param>
        /// <param name="sched">The sched.</param>
        /// <param name="cancellationToken">The cancellation instruction.</param>
        public virtual async Task ProcessStreamAndScheduleJobs(
            Stream stream,
            IScheduler sched,
            CancellationToken cancellationToken = default)
        {
            using (var sr = new StreamReader(stream))
            {
                ProcessInternal(await sr.ReadToEndAsync().ConfigureAwait(false));
            }
            await ExecutePreProcessCommands(sched, cancellationToken).ConfigureAwait(false);
            await ScheduleJobs(sched, cancellationToken).ConfigureAwait(false);
        }

        /// <summary>
        /// Schedules the given sets of jobs and triggers.
        /// </summary>
        /// <param name="sched">The sched.</param>
        /// <param name="cancellationToken">The cancellation instruction.</param>
        public virtual async Task ScheduleJobs(
            IScheduler sched,
            CancellationToken cancellationToken = default)
        {
            List<IJobDetail> jobs = new List<IJobDetail>(LoadedJobs);
            List<ITrigger> triggers = new List<ITrigger>(LoadedTriggers);

            Log.Info("Adding " + jobs.Count + " jobs, " + triggers.Count + " triggers.");

            IDictionary<JobKey, List<IMutableTrigger>> triggersByFQJobName = BuildTriggersByFQJobNameMap(triggers);

            // add each job, and it's associated triggers
            while (jobs.Count > 0)
            {
                // remove jobs as we handle them...
                IJobDetail detail = jobs[0];
                jobs.Remove(detail);

                IJobDetail? dupeJ = null;
                try
                {
                    // The existing job could have been deleted, and Quartz API doesn't allow us to query this without
                    // loading the job class, so use try/catch to handle it.
                    dupeJ = await sched.GetJobDetail(detail.Key, cancellationToken).ConfigureAwait(false);
                }
                catch (JobPersistenceException e)
                {
                    if (e.InnerException is TypeLoadException && OverWriteExistingData)
                    {
                        // We are going to replace jobDetail anyway, so just delete it first.
                        Log.Info("Removing job: " + detail.Key);
                        await sched.DeleteJob(detail.Key, cancellationToken).ConfigureAwait(false);
                    }
                    else
                    {
                        throw;
                    }
                }

                if (dupeJ != null)
                {
                    if (!OverWriteExistingData && IgnoreDuplicates)
                    {
                        Log.Info("Not overwriting existing job: " + dupeJ.Key);
                        continue; // just ignore the entry
                    }
                    if (!OverWriteExistingData && !IgnoreDuplicates)
                    {
                        throw new ObjectAlreadyExistsException(detail);
                    }
                }

                if (dupeJ != null)
                {
                    Log.Info("Replacing job: " + detail.Key);
                }
                else
                {
                    Log.Info("Adding job: " + detail.Key);
                }

                triggersByFQJobName.TryGetValue(detail.Key, out var triggersOfJob);

                if (!detail.Durable && (triggersOfJob == null || triggersOfJob.Count == 0))
                {
                    if (dupeJ == null)
                    {
                        throw new SchedulerException(
                            "A new job defined without any triggers must be durable: " +
                            detail.Key);
                    }

                    if (dupeJ.Durable && (await sched.GetTriggersOfJob(detail.Key, cancellationToken).ConfigureAwait(false)).Count == 0)
                    {
                        throw new SchedulerException(
                            "Can't change existing durable job without triggers to non-durable: " +
                            detail.Key);
                    }
                }


                if (dupeJ != null || detail.Durable)
                {
                    if (triggersOfJob != null && triggersOfJob.Count > 0)
                    {
                        await sched.AddJob(detail, true, true, cancellationToken).ConfigureAwait(false); // add the job regardless is durable or not b/c we have trigger to add
                    }
                    else
                    {
                        await sched.AddJob(detail, true, false, cancellationToken).ConfigureAwait(false); // add the job only if a replacement or durable, else exception will throw!
                    }
                }
                else
                {
                    bool addJobWithFirstSchedule = true;

                    // Add triggers related to the job...
                    while (triggersOfJob!.Count > 0)
                    {
                        IMutableTrigger trigger = triggersOfJob[0];
                        // remove triggers as we handle them...
                        triggersOfJob.Remove(trigger);

                        ITrigger? dupeT = await sched.GetTrigger(trigger.Key, cancellationToken).ConfigureAwait(false);
                        if (dupeT != null)
                        {
                            if (OverWriteExistingData)
                            {
                                if (Log.IsDebugEnabled())
                                {
                                    Log.DebugFormat("Rescheduling job: {0} with updated trigger: {1}", trigger.JobKey, trigger.Key);
                                }
                            }
                            else if (IgnoreDuplicates)
                            {
                                Log.Info("Not overwriting existing trigger: " + dupeT.Key);
                                continue; // just ignore the trigger (and possibly job)
                            }
                            else
                            {
                                throw new ObjectAlreadyExistsException(trigger);
                            }

                            if (!dupeT.JobKey.Equals(trigger.JobKey))
                            {
                                ReportDuplicateTrigger(trigger);
                            }

                            await DoRescheduleJob(sched, trigger, dupeT, cancellationToken).ConfigureAwait(false);
                        }
                        else
                        {
                            if (Log.IsDebugEnabled())
                            {
                                Log.DebugFormat("Scheduling job: {0} with trigger: {1}", trigger.JobKey, trigger.Key);
                            }

                            try
                            {
                                if (addJobWithFirstSchedule)
                                {
                                    await sched.ScheduleJob(detail, trigger, cancellationToken).ConfigureAwait(false); // add the job if it's not in yet...
                                    addJobWithFirstSchedule = false;
                                }
                                else
                                {
                                    await sched.ScheduleJob(trigger, cancellationToken).ConfigureAwait(false);
                                }
                            }
                            catch (ObjectAlreadyExistsException)
                            {
                                if (Log.IsDebugEnabled())
                                {
                                    Log.DebugFormat("Adding trigger: {0} for job: {1} failed because the trigger already existed.  "
                                        + "This is likely due to a race condition between multiple instances "
                                        + "in the cluster.  Will try to reschedule instead.", trigger.Key, detail.Key);
                                }
                                // Let's try one more time as reschedule.
                                var oldTrigger = await sched.GetTrigger(trigger.Key, cancellationToken).ConfigureAwait(false);
                                await DoRescheduleJob(sched, trigger, oldTrigger, cancellationToken).ConfigureAwait(false);
                            }
                        }
                    }
                }
            }

            // add triggers that weren't associated with a new job... (those we already handled were removed above)
            foreach (IMutableTrigger trigger in triggers)
            {
                ITrigger? dupeT = await sched.GetTrigger(trigger.Key, cancellationToken).ConfigureAwait(false);
                if (dupeT != null)
                {
                    if (OverWriteExistingData)
                    {
                        if (Log.IsDebugEnabled())
                        {
                            Log.DebugFormat("Rescheduling job: " + trigger.JobKey + " with updated trigger: " + trigger.Key);
                        }
                    }
                    else if (IgnoreDuplicates)
                    {
                        Log.Info("Not overwriting existing trigger: " + dupeT.Key);
                        continue; // just ignore the trigger
                    }
                    else
                    {
                        throw new ObjectAlreadyExistsException(trigger);
                    }

                    if (!dupeT.JobKey.Equals(trigger.JobKey))
                    {
                        ReportDuplicateTrigger(trigger);
                    }

                    await DoRescheduleJob(sched, trigger, dupeT, cancellationToken).ConfigureAwait(false);
                }
                else
                {
                    if (Log.IsDebugEnabled())
                    {
                        Log.DebugFormat("Scheduling job: {0} with trigger: {1}", trigger.JobKey, trigger.Key);
                    }

                    try
                    {
                        await sched.ScheduleJob(trigger, cancellationToken).ConfigureAwait(false);
                    }
                    catch (ObjectAlreadyExistsException)
                    {
                        if (Log.IsDebugEnabled())
                        {
                            Log.Debug(
                                "Adding trigger: " + trigger.Key + " for job: " + trigger.JobKey +
                                " failed because the trigger already existed.  " +
                                "This is likely due to a race condition between multiple instances " +
                                "in the cluster.  Will try to reschedule instead.");
                        }
                        // Let's rescheduleJob one more time.
                        var oldTrigger = await sched.GetTrigger(trigger.Key, cancellationToken).ConfigureAwait(false);
                        await DoRescheduleJob(sched, trigger, oldTrigger, cancellationToken).ConfigureAwait(false);
                    }
                }
            }
        }

        private void ReportDuplicateTrigger(IMutableTrigger trigger)
        {
            Log.WarnFormat("Possibly duplicately named ({0}) trigger in configuration, this can be caused by not having a fixed job key for targeted jobs", trigger.Key);
        }

        private Task DoRescheduleJob(
            IScheduler sched,
            IMutableTrigger trigger,
            ITrigger? oldTrigger,
            CancellationToken cancellationToken = default)
        {
            // if this is a trigger with default start time we can consider relative scheduling
            if (oldTrigger != null && trigger.StartTimeUtc - SystemTime.UtcNow() < TimeSpan.FromSeconds(5) && ScheduleTriggerRelativeToReplacedTrigger)
            {
                Log.DebugFormat("Using relative scheduling for trigger with key {0}", trigger.Key);

                var oldTriggerPreviousFireTime = oldTrigger.GetPreviousFireTimeUtc();
                trigger.StartTimeUtc = oldTrigger.StartTimeUtc;
                ((IOperableTrigger)trigger).SetPreviousFireTimeUtc(oldTriggerPreviousFireTime);
                // if oldTriggerPreviousFireTime is null then NextFireTime should be set relative to oldTrigger.StartTimeUtc 
                // to be able to handle misfiring for an existing trigger that has never been executed before.
                ((IOperableTrigger)trigger).SetNextFireTimeUtc(trigger.GetFireTimeAfter(oldTriggerPreviousFireTime ?? oldTrigger.StartTimeUtc));
            }

            return sched.RescheduleJob(trigger.Key, trigger, cancellationToken);
        }

        protected virtual IDictionary<JobKey, List<IMutableTrigger>> BuildTriggersByFQJobNameMap(List<ITrigger> triggers)
        {
            Dictionary<JobKey, List<IMutableTrigger>> triggersByFQJobName = new Dictionary<JobKey, List<IMutableTrigger>>();

            foreach (IMutableTrigger trigger in triggers)
            {
                if (!triggersByFQJobName.TryGetValue(trigger.JobKey, out var triggersOfJob))
                {
                    triggersOfJob = new List<IMutableTrigger>();
                    triggersByFQJobName[trigger.JobKey] = triggersOfJob;
                }
                triggersOfJob.Add(trigger);
            }

            return triggersByFQJobName;
        }

        protected async Task ExecutePreProcessCommands(
            IScheduler scheduler,
            CancellationToken cancellationToken = default)
        {
            foreach (string group in jobGroupsToDelete)
            {
                if (group.Equals("*"))
                {
                    Log.Info("Deleting all jobs in ALL groups.");
                    foreach (string groupName in await scheduler.GetJobGroupNames(cancellationToken).ConfigureAwait(false))
                    {
                        if (!jobGroupsToNeverDelete.Contains(groupName))
                        {
                            foreach (JobKey key in await scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(groupName), cancellationToken).ConfigureAwait(false))
                            {
                                await scheduler.DeleteJob(key, cancellationToken).ConfigureAwait(false);
                            }
                        }
                    }
                }
                else
                {
                    if (!jobGroupsToNeverDelete.Contains(group))
                    {
                        Log.InfoFormat("Deleting all jobs in group: {0}", group);
                        foreach (JobKey key in await scheduler.GetJobKeys(GroupMatcher<JobKey>.GroupEquals(group), cancellationToken).ConfigureAwait(false))
                        {
                            await scheduler.DeleteJob(key, cancellationToken).ConfigureAwait(false);
                        }
                    }
                }
            }

            foreach (string group in triggerGroupsToDelete)
            {
                if (group.Equals("*"))
                {
                    Log.Info("Deleting all triggers in ALL groups.");
                    foreach (string groupName in await scheduler.GetTriggerGroupNames(cancellationToken).ConfigureAwait(false))
                    {
                        if (!triggerGroupsToNeverDelete.Contains(groupName))
                        {
                            foreach (TriggerKey key in await scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.GroupEquals(groupName), cancellationToken).ConfigureAwait(false))
                            {
                                await scheduler.UnscheduleJob(key, cancellationToken).ConfigureAwait(false);
                            }
                        }
                    }
                }
                else
                {
                    if (!triggerGroupsToNeverDelete.Contains(group))
                    {
                        Log.InfoFormat("Deleting all triggers in group: {0}", group);
                        foreach (TriggerKey key in await scheduler.GetTriggerKeys(GroupMatcher<TriggerKey>.GroupEquals(group), cancellationToken).ConfigureAwait(false))
                        {
                            await scheduler.UnscheduleJob(key, cancellationToken).ConfigureAwait(false);
                        }
                    }
                }
            }

            foreach (JobKey key in jobsToDelete)
            {
                if (!jobGroupsToNeverDelete.Contains(key.Group))
                {
                    Log.InfoFormat("Deleting job: {0}", key);
                    await scheduler.DeleteJob(key, cancellationToken).ConfigureAwait(false);
                }
            }

            foreach (TriggerKey key in triggersToDelete)
            {
                if (!triggerGroupsToNeverDelete.Contains(key.Group))
                {
                    Log.InfoFormat("Deleting trigger: {0}", key);
                    await scheduler.UnscheduleJob(key, cancellationToken).ConfigureAwait(false);
                }
            }
        }


        /// <summary>
        /// Adds a detected validation exception.
        /// </summary>
        /// <param name="e">The exception.</param>
        protected virtual void AddValidationException(XmlException e)
        {
            validationExceptions.Add(e);
        }

        /// <summary>
        /// Resets the number of detected validation exceptions.
        /// </summary>
        protected virtual void ClearValidationExceptions()
        {
            validationExceptions.Clear();
        }

        /// <summary>
        /// Throws a ValidationException if the number of validationExceptions
        /// detected is greater than zero.
        /// </summary>
        /// <exception cref="ValidationException">
        /// DTD validation exception.
        /// </exception>
        protected virtual void MaybeThrowValidationException()
        {
            if (validationExceptions.Count > 0)
            {
                throw new ValidationException(validationExceptions);
            }
        }

        public void AddJobGroupToNeverDelete(string jobGroupName)
        {
            jobGroupsToNeverDelete.Add(jobGroupName);
        }

        public void AddTriggerGroupToNeverDelete(string triggerGroupName)
        {
            triggerGroupsToNeverDelete.Add(triggerGroupName);
        }


        /// <summary>
        /// Helper class to map constant names to their values.
        /// </summary>
        internal class Constants
        {
            private readonly Type[] types;

            public Constants(params Type[] reflectedTypes)
            {
                types = reflectedTypes;
            }

            public int AsNumber(string field)
            {
                foreach (Type type in types)
                {
                    FieldInfo? fi = type.GetField(field);
                    if (fi != null)
                    {
                        return Convert.ToInt32(fi.GetValue(null), CultureInfo.InvariantCulture);
                    }
                }

                // not found
                throw new Exception($"Unknown field '{field}'");
            }
        }
    }
}

JobSchedulingData_2.0 源码

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.17929
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

//
// This source code was auto-generated by xsd, Version=4.0.30319.17929.
//

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Xml.Serialization;

namespace Quartz.Xml.JobSchedulingData_2.0 {
    
    /// <remarks/>
    [Serializable()]
    [DebuggerStepThrough()]
    [XmlType(AnonymousType=true, Namespace="http://quartznet.sourceforge.net/JobSchedulingData")]
    [XmlRoot("job-scheduling-data", Namespace="http://quartznet.sourceforge.net/JobSchedulingData", IsNullable=false)]
    public partial class QuartzXmlConfiguration20 {

        private preprocessingcommandsType[] preprocessingcommandsField;

        private processingdirectivesType[] processingdirectivesField;

        private jobschedulingdataSchedule[] scheduleField;

        private string versionField;

        /// <remarks/>
        [XmlElement("pre-processing-commands")]
        public preprocessingcommandsType[] preprocessingcommands {
            get {
                return this.preprocessingcommandsField;
            }
            set {
                this.preprocessingcommandsField = value;
            }
        }

        /// <remarks/>
        [XmlElement("processing-directives")]
        public processingdirectivesType[] processingdirectives {
            get {
                return this.processingdirectivesField;
            }
            set {
                this.processingdirectivesField = value;
            }
        }

        /// <remarks/>
        [XmlElement("schedule")]
        public jobschedulingdataSchedule[] schedule {
            get {
                return this.scheduleField;
            }
            set {
                this.scheduleField = value;
            }
        }

        /// <remarks/>
        [XmlAttribute()]
        public string version {
            get {
                return this.versionField;
            }
            set {
                this.versionField = value;
            }
        }
    }

    /// <remarks/>
    [Serializable()]
    [DebuggerStepThrough()]
    [XmlType(TypeName="pre-processing-commandsType", Namespace="http://quartznet.sourceforge.net/JobSchedulingData")]
    public partial class preprocessingcommandsType {

        private string[] deletejobsingroupField;

        private string[] deletetriggersingroupField;

        private preprocessingcommandsTypeDeletejob[] deletejobField;

        private preprocessingcommandsTypeDeletetrigger[] deletetriggerField;

        <!-- 清除调度器中组内的所有作业 -->
        /// <remarks/>
        [XmlElement("delete-jobs-in-group")]
        public string[] deletejobsingroup {
            get {
                return this.deletejobsingroupField;
            }
            set {
                this.deletejobsingroupField = value;
            }
        }

        <!-- 清除调度器中组内的所有触发器 -->
        /// <remarks/>
        [XmlElement("delete-triggers-in-group")]
        public string[] deletetriggersingroup {
            get {
                return this.deletetriggersingroupField;
            }
            set {
                this.deletetriggersingroupField = value;
            }
        }

        <!-- 清除调度器中的作业 -->
        /// <remarks/>
        [XmlElement("delete-job")]
        public preprocessingcommandsTypeDeletejob[] deletejob {
            get {
                return this.deletejobField;
            }
            set {
                this.deletejobField = value;
            }
        }

        <!-- 清除调度器中的作业 -->
        /// <remarks/>
        [XmlElement("delete-trigger")]
        public preprocessingcommandsTypeDeletetrigger[] deletetrigger {
            get {
                return this.deletetriggerField;
            }
            set {
                this.deletetriggerField = value;
            }
        }
    }

    /// <remarks/>
    [Serializable()]
    [DebuggerStepThrough()]
    [XmlType(AnonymousType=true, Namespace="http://quartznet.sourceforge.net/JobSchedulingData")]
    public partial class preprocessingcommandsTypeDeletejob {

        private string nameField;

        private string groupField;

        /// <remarks/>
        public string name {
            get {
                return this.nameField;
            }
            set {
                this.nameField = value;
            }
        }

        /// <remarks/>
        public string group {
            get {
                return this.groupField;
            }
            set {
                this.groupField = value;
            }
        }
    }

    /// <remarks/>
    [XmlInclude(typeof(calendarIntervalTriggerType))]
    [XmlInclude(typeof(cronTriggerType))]
    [XmlInclude(typeof(simpleTriggerType))]
    [Serializable()]
    [DebuggerStepThrough()]
    [XmlType(Namespace="http://quartznet.sourceforge.net/JobSchedulingData")]
    public abstract partial class abstractTriggerType {

        private string nameField;

        private string groupField;

        private string descriptionField;

        private string jobnameField;

        private string jobgroupField;

        private string priorityField;

        private string calendarnameField;

        private jobdatamapType jobdatamapField;

        private object itemField;

        private DateTime endtimeField;

        private bool endtimeFieldSpecified;

        /// <remarks/>
        public string name {
            get {
                return this.nameField;
            }
            set {
                this.nameField = value;
            }
        }

        /// <remarks/>
        public string group {
            get {
                return this.groupField;
            }
            set {
                this.groupField = value;
            }
        }

        /// <remarks/>
        public string description {
            get {
                return this.descriptionField;
            }
            set {
                this.descriptionField = value;
            }
        }

        /// <remarks/>
        [XmlElement("job-name")]
        public string jobname {
            get {
                return this.jobnameField;
            }
            set {
                this.jobnameField = value;
            }
        }

        /// <remarks/>
        [XmlElement("job-group")]
        public string jobgroup {
            get {
                return this.jobgroupField;
            }
            set {
                this.jobgroupField = value;
            }
        }

        /// <remarks/>
        [XmlElement(DataType="nonNegativeInteger")]
        public string priority {
            get {
                return this.priorityField;
            }
            set {
                this.priorityField = value;
            }
        }

        /// <remarks/>
        [XmlElement("calendar-name")]
        public string calendarname {
            get {
                return this.calendarnameField;
            }
            set {
                this.calendarnameField = value;
            }
        }

        /// <remarks/>
        [XmlElement("job-data-map")]
        public jobdatamapType jobdatamap {
            get {
                return this.jobdatamapField;
            }
            set {
                this.jobdatamapField = value;
            }
        }

        /// <remarks/>
        [XmlElement("start-time", typeof(DateTime))]
        [XmlElement("start-time-seconds-in-future", typeof(string), DataType="nonNegativeInteger")]
        public object Item {
            get {
                return this.itemField;
            }
            set {
                this.itemField = value;
            }
        }

        /// <remarks/>
        [XmlElement("end-time")]
        public DateTime endtime {
            get {
                return this.endtimeField;
            }
            set {
                this.endtimeField = value;
            }
        }

        /// <remarks/>
        [XmlIgnore()]
        public bool endtimeSpecified {
            get {
                return this.endtimeFieldSpecified;
            }
            set {
                this.endtimeFieldSpecified = value;
            }
        }
    }

    /// <remarks/>
    [Serializable()]
    [DebuggerStepThrough()]
    [XmlType(TypeName="job-data-mapType", Namespace="http://quartznet.sourceforge.net/JobSchedulingData")]
    public partial class jobdatamapType {

        private entryType[] entryField;

        /// <remarks/>
        [XmlElement("entry")]
        public entryType[] entry {
            get {
                return this.entryField;
            }
            set {
                this.entryField = value;
            }
        }
    }

    /// <remarks/>
    [Serializable()]
    [DebuggerStepThrough()]
    [XmlType(Namespace="http://quartznet.sourceforge.net/JobSchedulingData")]
    public partial class entryType {

        private string keyField;

        private string valueField;

        /// <remarks/>
        public string key {
            get {
                return this.keyField;
            }
            set {
                this.keyField = value;
            }
        }

        /// <remarks/>
        public string value {
            get {
                return this.valueField;
            }
            set {
                this.valueField = value;
            }
        }
    }

    /// <remarks/>
    [Serializable()]
    [DebuggerStepThrough()]
    [XmlType(Namespace="http://quartznet.sourceforge.net/JobSchedulingData")]
    public partial class calendarIntervalTriggerType : abstractTriggerType {

        private string misfireinstructionField;

        private string repeatintervalField;

        private string repeatintervalunitField;

        /// <remarks/>
        [XmlElement("misfire-instruction")]
        public string misfireinstruction {
            get {
                return this.misfireinstructionField;
            }
            set {
                this.misfireinstructionField = value;
            }
        }

        /// <remarks/>
        [XmlElement("repeat-interval", DataType="nonNegativeInteger")]
        public string repeatinterval {
            get {
                return this.repeatintervalField;
            }
            set {
                this.repeatintervalField = value;
            }
        }

        /// <remarks/>
        [XmlElement("repeat-interval-unit")]
        public string repeatintervalunit {
            get {
                return this.repeatintervalunitField;
            }
            set {
                this.repeatintervalunitField = value;
            }
        }
    }

    /// <remarks/>
    [Serializable()]
    [DebuggerStepThrough()]
    [XmlType(Namespace="http://quartznet.sourceforge.net/JobSchedulingData")]
    public partial class cronTriggerType : abstractTriggerType {

        private string misfireinstructionField;

        private string cronexpressionField;

        private string timezoneField;

        /// <remarks/>
        [XmlElement("misfire-instruction")]
        public string misfireinstruction {
            get {
                return this.misfireinstructionField;
            }
            set {
                this.misfireinstructionField = value;
            }
        }

        /// <remarks/>
        [XmlElement("cron-expression")]
        public string cronexpression {
            get {
                return this.cronexpressionField;
            }
            set {
                this.cronexpressionField = value;
            }
        }

        /// <remarks/>
        [XmlElement("time-zone")]
        public string timezone {
            get {
                return this.timezoneField;
            }
            set {
                this.timezoneField = value;
            }
        }
    }

    /// <remarks/>
    [Serializable()]
    [DebuggerStepThrough()]
    [XmlType(Namespace="http://quartznet.sourceforge.net/JobSchedulingData")]
    public partial class simpleTriggerType : abstractTriggerType {

        private string misfireinstructionField;

        private string repeatcountField;

        private string repeatintervalField;

        /// <remarks/>
        [XmlElement("misfire-instruction")]
        public string misfireinstruction {
            get {
                return this.misfireinstructionField;
            }
            set {
                this.misfireinstructionField = value;
            }
        }

        /// <remarks/>
        [XmlElement("repeat-count", DataType="integer")]
        public string repeatcount {
            get {
                return this.repeatcountField;
            }
            set {
                this.repeatcountField = value;
            }
        }

        /// <remarks/>
        [XmlElement("repeat-interval", DataType="nonNegativeInteger")]
        public string repeatinterval {
            get {
                return this.repeatintervalField;
            }
            set {
                this.repeatintervalField = value;
            }
        }
    }

    /// <remarks/>
    [Serializable()]
    [DebuggerStepThrough()]
    [XmlType(Namespace="http://quartznet.sourceforge.net/JobSchedulingData")]
    public partial class triggerType {

        private abstractTriggerType itemField;

        /// <remarks/>
        [XmlElement("calendar-interval", typeof(calendarIntervalTriggerType))]
        [XmlElement("cron", typeof(cronTriggerType))]
        [XmlElement("simple", typeof(simpleTriggerType))]
        public abstractTriggerType Item {
            get {
                return this.itemField;
            }
            set {
                this.itemField = value;
            }
        }
    }

    /// <remarks/>
    [Serializable()]
    [DebuggerStepThrough()]
    [XmlType(TypeName="job-detailType", Namespace="http://quartznet.sourceforge.net/JobSchedulingData")]
    public partial class jobdetailType {

        private string nameField;

        private string groupField;

        private string descriptionField;

        private string jobtypeField;

        private bool durableField;

        private bool recoverField;

        private jobdatamapType jobdatamapField;

        /// <remarks/>
        public string name {
            get {
                return this.nameField;
            }
            set {
                this.nameField = value;
            }
        }

        /// <remarks/>
        public string group {
            get {
                return this.groupField;
            }
            set {
                this.groupField = value;
            }
        }

        /// <remarks/>
        public string description {
            get {
                return this.descriptionField;
            }
            set {
                this.descriptionField = value;
            }
        }

        /// <remarks/>
        [XmlElement("job-type")]
        public string jobtype {
            get {
                return this.jobtypeField;
            }
            set {
                this.jobtypeField = value;
            }
        }

        /// <remarks/>
        public bool durable {
            get {
                return this.durableField;
            }
            set {
                this.durableField = value;
            }
        }

        /// <remarks/>
        public bool recover {
            get {
                return this.recoverField;
            }
            set {
                this.recoverField = value;
            }
        }

        /// <remarks/>
        [XmlElement("job-data-map")]
        public jobdatamapType jobdatamap {
            get {
                return this.jobdatamapField;
            }
            set {
                this.jobdatamapField = value;
            }
        }
    }

    /// <remarks/>
    [Serializable()]
    [DebuggerStepThrough()]
    [XmlType(TypeName="processing-directivesType", Namespace="http://quartznet.sourceforge.net/JobSchedulingData")]
    public partial class processingdirectivesType {

        private bool overwriteexistingdataField;

        private bool ignoreduplicatesField;

        private bool scheduletriggerrelativetoreplacedtriggerField;

        public processingdirectivesType() {
            this.overwriteexistingdataField = true;
            this.ignoreduplicatesField = false;
            this.scheduletriggerrelativetoreplacedtriggerField = false;
        }

       <!-- 如果调度程序中有任何同名的作业/触发器(与此文件中相同),会覆盖它们 -->
        /// <remarks/>
        [XmlElement("overwrite-existing-data")]
        [DefaultValue(true)]
        public bool overwriteexistingdata {
            get {
                return this.overwriteexistingdataField;
            }
            set {
                this.overwriteexistingdataField = value;
            }
        }

        <!-- 如果调度程序中有任何同名的作业/触发器(与此文件中相同),并且覆盖为false,则忽略它们,而不是生成错误 -->
        /// <remarks/>
        [XmlElement("ignore-duplicates")]
        [DefaultValue(false)]
        public bool ignoreduplicates {
            get {
                return this.ignoreduplicatesField;
            }
            set {
                this.ignoreduplicatesField = value;
            }
        }

        /// <remarks/>
        [XmlElement("schedule-trigger-relative-to-replaced-trigger")]
        [DefaultValue(false)]
        public bool scheduletriggerrelativetoreplacedtrigger {
            get {
                return this.scheduletriggerrelativetoreplacedtriggerField;
            }
            set {
                this.scheduletriggerrelativetoreplacedtriggerField = value;
            }
        }
    }

    /// <remarks/>
    [Serializable()]
    [DebuggerStepThrough()]
    [XmlType(AnonymousType=true, Namespace="http://quartznet.sourceforge.net/JobSchedulingData")]
    public partial class preprocessingcommandsTypeDeletetrigger {

        private string nameField;

        private string groupField;

        /// <remarks/>
        public string name {
            get {
                return this.nameField;
            }
            set {
                this.nameField = value;
            }
        }

        /// <remarks/>
        public string group {
            get {
                return this.groupField;
            }
            set {
                this.groupField = value;
            }
        }
    }

    /// <remarks/>
    [Serializable()]
    [DebuggerStepThrough()]
    [XmlType(AnonymousType=true, Namespace="http://quartznet.sourceforge.net/JobSchedulingData")]
    public partial class jobschedulingdataSchedule {

        private jobdetailType[] jobField;

        private triggerType[] triggerField;

        /// <remarks/>
        [XmlElement("job")]
        public jobdetailType[] job {
            get {
                return this.jobField;
            }
            set {
                this.jobField = value;
            }
        }

        /// <remarks/>
        [XmlElement("trigger")]
        public triggerType[] trigger {
            get {
                return this.triggerField;
            }
            set {
                this.triggerField = value;
            }
        }
    }
}

posted @ 2023-03-06 17:43  誓鼎  阅读(41)  评论(0编辑  收藏  举报