30

I wonder how to pass an instance variable externally in Quartz?

Below is pseudo code I would like to write. How can I pass externalInstance into this Job?

public class SimpleJob implements Job {
        @Override
        public void execute(JobExecutionContext context)
                throws JobExecutionException {

            float avg = externalInstance.calculateAvg();
        }
}
janetsmith
  • 8,562
  • 11
  • 58
  • 76

6 Answers6

61

you can put your instance in the schedulerContext.When you are going to schedule the job ,just before that you can do below:

getScheduler().getContext().put("externalInstance", externalInstance);

Your job class would be like below:

public class SimpleJob implements Job {
    @Override
    public void execute(JobExecutionContext context)
            throws JobExecutionException {
        SchedulerContext schedulerContext = null;
        try {
            schedulerContext = context.getScheduler().getContext();
        } catch (SchedulerException e1) {
            e1.printStackTrace();
        }
        ExternalInstance externalInstance =
            (ExternalInstance) schedulerContext.get("externalInstance");

        float avg = externalInstance.calculateAvg();
    }
}

If you are using Spring ,you can actually using spring's support to inject the whole applicationContext like answered in the Link

Dennis
  • 4,011
  • 7
  • 36
  • 50
Rips
  • 1,964
  • 1
  • 23
  • 45
  • This works fine for serializable objects, and it'll work for classes that you have access to. What if I want to inject something from an external jar? For instance, I'm trying to write a StatefulJob that lists all the files in an S3 bucket since a certain date and stores the files it listed in JobDetailMap. I want to inject a spring configured AmazonS3Client into my job. I cannot do this because that class is not serializable, and so I have to inject the secret key, access key and write code to manage creation of an s3 client. Is that the best way to do it? Any suggestions would be helpful. – Vivek Rao Dec 26 '13 at 04:56
  • Answering my own question - autowire the job, found this really good solution online - http://stackoverflow.com/questions/4258313/how-to-use-autowired-in-a-quartz-job – Vivek Rao Dec 27 '13 at 14:56
  • I was having a tough time to get a Spring managed bean in my Job class. I tried setting my object to JobDetail map but it could not serialize and was failing. Setting in scheduler context worked like a charm. Thank you! – JavaYouth Apr 23 '18 at 05:26
  • what if you are passing different instance of object for every schedule you made? i mean the object parameter should be tied to job not the scheduler – lorraine batol Jul 25 '19 at 10:01
  • Best and simple answer i have found. Thanks! – Lior Feb 04 '20 at 07:37
10

While scheduling the job using a trigger, you would have defined JobDataMap that is added to the JobDetail. That JobDetail object will be present in the JobExecutionContext passed to the execute() method in your Job. So, you should figure out a way to pass your externalInstance through the JobDataMap. HTH.

Vikdor
  • 23,934
  • 10
  • 61
  • 84
  • external instance I mean could be any custom data type, not limited to native data type, like String, Float, Int etc. I read from the documentation, it says that instance needs to be serializable. – janetsmith Oct 08 '12 at 08:31
  • In our case, the objects to work on are DB objects, so we used store the criteria to retrieve them from DB, in the JobDataMap and retrieve them in the execute method. So, you also need to figure out a way to pass the information required to retrieve the external instance from some store iff you cannot make all of them serializable. – Vikdor Oct 08 '12 at 08:36
  • The answer didn't added a value, actually I came to this question trying to figure out this "way" – Mohammed Elrashidy Nov 07 '19 at 13:04
  • @MohammedElrashidy, one of the ways is to store the jobdatamap (key, value) pairs in the database. This is under the assumption that the values are serializable. If they are not serializable, then more details on the nature of such data will help suggest more "ways" to pass this information. HTH. – Vikdor Nov 08 '19 at 19:17
  • @Vikdor I understand, my concern was that your answer was really general without giving real working answer. Thanks, it is still giving hints BTW I was able to pass instances and left that as an answer in this question https://stackoverflow.com/a/58801887/2152704 – Mohammed Elrashidy Nov 11 '19 at 13:03
4

Add the object to the JobDataMap:

JobDetail job = JobBuilder.newJob(MyJobClass.class)
                          .withIdentity("MyIdentity",
                                        "MyGroup")
                          .build();
job.getJobDataMap()
   .put("MyObject",
        myObject);

Access the data from the JobDataMap:

var myObject = (MyObjectClass) context.getJobDetail()
                                      .getJobDataMap()
                                      .get("carrier");
Evvo
  • 481
  • 5
  • 8
2

Solve this problem by creating one interface with one HashMap putting required information there.

Implement this interface in your Quartz Job class this information will be accessible.

In IFace

Map<JobKey,Object> map = new HashMap<>();

In Job

map.get(context.getJobDetail().getKey()) =>  will give you Object
James Monger
  • 10,181
  • 7
  • 62
  • 98
Harsh Maheswari
  • 467
  • 5
  • 3
2

Quartz has a simple way to grep params from JobDataMap using setters

I am using Quartz 2.3 and I simply used setter to fetch passed instance objects

For example I created this class

public class Data implements Serializable {
    @JsonProperty("text")
    private String text;

    @JsonCreator
    public Data(@JsonProperty("b") String text) {this.text = text;}

    public String getText() {return text;}
}

Then I created an instance of this class and put it inside the JobDataMap

JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put("data", new Data(1, "One!"));
JobDetail job = newJob(HelloJob.class)
                .withIdentity("myJob", "group")
                .withDescription("bla bla bla")
                .usingJobData(jobDataMap) // <!!!
                .build();

And my job class looks like this

public class HelloJob implements Job {
    Data data;

    public HelloJob() {}

    public void execute(JobExecutionContext context)
            throws JobExecutionException
    {
        String text = data.getText();
        System.out.println(text);
    }

    public void setData(Data data) {this.data = data;}
}
  • Note: it is mandatory that the field and the setter matched the key

This code will print One! when you schedule the job.

That's it, clean and efficient

Mohammed Elrashidy
  • 1,820
  • 1
  • 14
  • 16
0

This is the responsibility of the JobFactory. The default PropertySettingJobFactory implementation will invoke any bean-setter methods, based on properties found in the schdeuler context, the trigger, and the job detail. So as long as you have implemnted an appropriate setContext() setter method you should be able to do any of the following:

scheduler.getContext().put("context", context);

Or

Trigger trigger = TriggerBuilder.newTrigger()
  ...
  .usingJobData("context", context)
  .build()

Or

JobDetail job = JobBuilder.newJob(SimpleJob.class)
  ...
  .usingJobData("context", context)
  .build()

Or if that isn't enough you can provide your own JobFactory class which instantiates the Job objects however you please.

Matthew
  • 10,361
  • 5
  • 42
  • 54