问题
I have a following problem with Quartz JobDataMap. I expect that when using simple Quartz Job and passing not-primitive object (e.g. instance of StringBuilder) into JobDateMap, method execute (from my job) should be always invoked with different copy of objected I put. Unfortunately I always get instance of object I put into JobDateMap (like it would be a StatefulJob).
In bellow example I expect to get single '*' in every invocation while I get one more '*' every time.
public class MyJob implements Job {
public static void main(String[] args) throws SchedulerException {
SchedulerFactory schedFact = new StdSchedulerFactory();
Scheduler sched = schedFact.getScheduler();
JobDetail jobDetail = new JobDetail("job", Scheduler.DEFAULT_GROUP, MyJob.class);
jobDetail.getJobDataMap().put("param", new StringBuilder());
Trigger trigger = TriggerUtils.makeImmediateTrigger("trigger", 10, 100);
trigger.setGroup(Scheduler.DEFAULT_GROUP);
sched.scheduleJob(jobDetail, trigger);
sched.start();
try {
Thread.sleep(1000L);
} catch (Exception e) {}
sched.shutdown(true);
}
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
StringBuilder sb = (StringBuilder) context.getMergedJobDataMap().get("param");
sb.append("*");
System.out.println(sb.toString());
}
}
I think, I'm missing something about how Quartz is working. Anybody knows what?
回答1:
"Only store primitive data types (including Strings) in JobDataMap to avoid data serialization issues short and long-term."
source: http://quartz-scheduler.org/documentation/best-practices
回答2:
There are other facilities in Quartz that allow you to pass non-primitives in a more optimized fashion. Check out SchedulerContext class functionality.
using System;
using System.Text;
using Quartz;
using Quartz.Impl;
namespace QuartzNET.Samples
{
class Program
{
static void Main(string[] args)
{
// Create RAMJobStore instance
DirectSchedulerFactory.Instance.CreateVolatileScheduler(1);
ISchedulerFactory factory = DirectSchedulerFactory.Instance;
// Get scheduler and add object
IScheduler scheduler = factory.GetScheduler();
scheduler.Context.Add("History", new StringBuilder("Runtime History: "));
// Create job and trigger
IJobDetail job = JobBuilder.Create<MyJob>()
.WithIdentity("MyJob")
.Build();
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("Trigger")
.StartNow()
.WithSimpleSchedule(x => x
.WithInterval(TimeSpan.FromMinutes(1))
.RepeatForever())
.Build();
// Run it all
scheduler.Start();
scheduler.ScheduleJob(job, trigger);
}
}
class MyJob : IJob
{
public void Execute(IJobExecutionContext context)
{
var history = context.Scheduler.Context["History"] as StringBuilder;
history.AppendLine(context.NextFireTimeUtc.ToString());
Console.WriteLine(context.NextFireTimeUtc);
}
}
}
回答3:
I used JSON to send complex objects to job. It's not very smart but works for my pursposes.
In your application:
jobDetail.getJobDataMap().put("YOUR_PARAM_NAME", yourObject.toJson());
In your job:
JobDataMap dataMap = context.getJobDetail().getJobDataMap();
String jsonObject = dataMap.getString("YOUR_PARAM_NAME");
YourClass yourObject = YourClass.fromJson(jsonObject);
回答4:
As per Best Practices of JobDataMap, we should not put complex objects in JobDataMap to avoid short term or long term issues. (There might be issues of serialization which of course will not be solved by modifying Complex object class to implement Serializable.)
Workaround: Pass complex objects as a Json String and deserialize them after retrieving it from JobDataMap.
For example,
class OrderLine {
private long orderLineId;
private Item item;
}
class Item {
private long itemId;
private String itemName;
}
//Putting OrderLine object in JobDataMap
jobDetail.getJobDataMap().put("complexData", new Gson().toJson(new OrderLine()));
// Retrieving data from JobDataMap
String complexDataString =
context.getJobDetail().getJobDataMap().getString("complexData");
OrderLine orderLine = new Gson().fromJson(complexDataString, OrderLine.class);
回答5:
I was hitting the same error in our project, but realized that I had a different setting that was blocking it. You can use non "property-related" objects, if you configure it. You're looking for this value in the config file for quartz settings.
<add key="quartz.jobStore.useProperties" value="false" />
False allows you to use other objects other than just properties. Once I changed this ( mine was true ), then it started working for me.
来源:https://stackoverflow.com/questions/14562379/quartz-jobdatamap-doesnt-work-for-not-primitive-types