disasterkid
disasterkid

Reputation: 7278

"Scheduler already exists issue" persists

Even though I have used Tarun Arora's solution to the

Scheduler with the name xxx already exists.

issue, I still get the same error. This is his solution and I assume he has already implemented the Singleton pattern correctly:

public class Scheduler
{
    public readonly IScheduler Instance;
    public string Address { get; private set; }
    public string JobName { get; set; }
    public string JobGroup { get; set; }
    public int Priority { get; set; }
    public string CronExpression { get; set; }

    private readonly ISchedulerFactory _schedulerFactory;


    public Scheduler(string server, int port, string scheduler)
    {
        Address = string.Format("tcp://{0}:{1}/{2}", server, port, scheduler);
        _schedulerFactory = new StdSchedulerFactory(GetProperties(Address));

        try
        {
            Instance = _schedulerFactory.GetScheduler();

            if (!Instance.IsStarted)
                Instance.Start();
        }
        catch (SchedulerException ex)
        {
            throw new Exception(string.Format("Failed: {0}", ex.Message));
        }
    }

    private static NameValueCollection GetProperties(string address) 
    {
        var properties = new NameValueCollection();
        properties["quartz.scheduler.instanceName"] = "ServerScheduler";
        properties["quartz.scheduler.proxy"] = "true";
        properties["quartz.threadPool.threadCount"] = "0";
        properties["quartz.scheduler.proxy.address"] = address;
        return properties;
    }

    public IScheduler GetScheduler()
    {
        return Instance;
    }
}

I must confess I am not using the last part in which he says

If you wanted to call Scheduler then you should be using something like the code sample below…

public SchedulerMetaData GetMetaData()
{
        var scheduler = new Scheduler("ServerName", Convert.ToInt32("PortNumber"), "Scheduler"));
        return scheduler.GetMetaData();
}

This how I am calling the scheduler in my application:

IScheduler sched = new Scheduler(logger, "127.0.0.1", 555, "QuartzScheduler").Instance;
IJobDetail postbagjob = null;
ITrigger postbagJobTrigger = null;
try
{
    postbagjob = JobBuilder.Create<PostbagJob>()
        .WithIdentity(jobName, jobGroup)
        .UsingJobData("CampaignId", campaignId.ToString())
        .UsingJobData("CampaignType", campaignType)
        .Build();
    postbagJobTrigger = (ICronTrigger)TriggerBuilder.Create()
        .WithIdentity(triggerName, triggerGroup)
        .WithCronSchedule(cron)
        .StartAt(DateTime.Now)
        .Build();
}
catch (SchedulerException ex)
{
    MsgBox.Show(ex.Message);
}

It works the first time I am about to add the job, but the second time I call it, sched becomes null.

Upvotes: 3

Views: 4989

Answers (2)

Cryptc
Cryptc

Reputation: 3595

For the Quartz web service, restarting the app pool fixed this issue.

Upvotes: 0

LeftyX
LeftyX

Reputation: 35587

I've tested that implementation and it seems to work.
There must be something else in your solution which prevents things from working properly.

Are you sure that the job which is running in your server is the PostbagJob ?

I would advise you to check the configuration of your server. Make sure that you have something like this:

<add key="quartz.scheduler.exporter.type" value="Quartz.Simpl.RemotingSchedulerExporter, Quartz"/>
<add key="quartz.scheduler.exporter.port" value="555"/>
<add key="quartz.scheduler.exporter.bindName" value="QuartzScheduler"/>
<add key="quartz.scheduler.exporter.channelType" value="tcp"/>
<add key="quartz.scheduler.exporter.channelName" value="httpQuartz"/>

What can you tell us about your cron trigger? Can you post the cron expression?
Why don't you try to change it to a simple trigger and see how it works:

postbagJobTrigger = (ISimpleTrigger)TriggerBuilder.Create()
        .WithIdentity("SampleTrigger", "QUARTZGROUP")
        .WithSimpleSchedule(x => x.WithIntervalInSeconds(2).RepeatForever())
        .StartNow()
        .Build();

Another thing that you should do is share an assembly with jobs between server and client so that they have the same signature.

I made a few changes to the Scheduler class. You can find the end result here.

NOTE:

To run the server and the client, make sure you have multiple startup projects configured in your solution (right click on solution).

UPDATE:

A better approach is to create a singleton class this way:

public sealed class QuartzScheduler
{
    private static QuartzScheduler instance = null;

    private static readonly object padlock = new object();

    private readonly ISchedulerFactory SchedulerFactory = null;
    private readonly IScheduler Scheduler = null;

    QuartzScheduler()
    {
        this.SchedulerFactory = new StdSchedulerFactory();
        this.Scheduler = this.SchedulerFactory.GetScheduler();
    }

    public static IScheduler Instance
    {
        get
        {
            lock (padlock)
            {
                if (instance == null)
                {
                    instance = new QuartzScheduler();
                }
                return instance.Scheduler;
            }
        }
    }
}

If you want to read more you can find lot of useful info on Jon Skeet's blog.
NB: I've used the third version.

Now you can simply use that object in your windows forms application:

QuartzScheduler.Instance.ScheduleJob(postbagjob, postbagJobTrigger);

No more than that.

If you check my github repo you can find a working solution.
I've moved the client configuration in the app.config file; I guess it's easier to customize:

<quartz>
    <add key="quartz.threadPool.type" value="Quartz.Simpl.ZeroSizeThreadPool, Quartz" />
    <add key="quartz.scheduler.instanceName" value="RemoteClient"/>
    <add key="quartz.threadPool.threadCount" value="0"/>
    <add key="quartz.scheduler.proxy" value="true"/>
    <add key="quartz.scheduler.proxy.address" value="tcp://127.0.0.1:555/QuartzScheduler"/>
</quartz>

Another thing to remember is your client is scheduling jobs, not really running the scheduler, so you should configure your thread pool as ZeroSizeThreadPool.

Upvotes: 2

Related Questions