Dynamically loading name of Java class file in a Quartz job - java

I have a Quartz job written in Java which runs fine if I have the Quartz JobDetail line set as follows:
JobDetail jd = new JobDetail("FeedMinersJob", scheduler.DEFAULT_GROUP, FeedMinersScheduler.class);
But I would like to dynamically load the class because the job details are stored in a database table. So I want something like this:
JobDetail jd = new JobDetail(sj.getJobName(), scheduler.DEFAULT_GROUP, sj.getJobClassFile());
Where sj is a scheduled job object and method sj.getJobClassFile() returns the name of the class defined in sj instead of having the class name hardcoded.
I've tried permutations of the Java Class.forName method but without success.

I ran into the same issue.
It does not output anything, and does not throw an error either.
This is because your job class doesn't have an empty constructor. So even though the code is correct, it has no mechanism to create the job object.
If you add an empty constructor to your Job class, itwill work.

As I understand it, you code should like this:
Class<?> jobClass = Class.forName(sj.getJobClassFile());
JobDetail jd = new JobDetail(sj.getJobName(), scheduler.DEFAULT_GROUP, jobClass);
Can you please post the code snippets which didn't work?

Try this
try {
Class<?> jobClass = Class.forName(sj.getJobClassFile());
JobDetail jd = new JobDetail(sj.getJobName(), scheduler.DEFAULT_GROUP, jobClass);
} catch (ClassNotFoundException e) {
// put here some error handling
}
And if it doesn't work please give more details about the problem - compilation error, exception in runtime or some other problem.

I have this, maybe it will be usefull to you: (getClassName() returns a string)
Class<?> jobClass = Class.forName(t_job.getClassName());
if (Job.class.isAssignableFrom(jobClass)) {
// create a job detail that is not volatile and is durable (is persistent and exists without trigger)
JobDetail job = new JobDetail(t_job.getName(), t_job.getGroupName(), jobClass, false, true, true);
job.setDescription(t_job.getDescription());
}

Related

how to delete specific trigger for a scheduled job

i have created job with like creating with one jobName and different keys(see JobDataMap)
CronTriggerImpl trigger = new CronTriggerImpl();
JobDetailImpl jobDetail = null;
trigger.setMisfireInstruction(CronTrigger.MISFIRE_INSTRUCTION_DO_NOTHING);
trigger.setName(getUniqueJobId());
trigger.setCronExpression(cronExpression);
trigger.setTimeZone(timeZone);
JobDataMap dataMap = new JobDataMap();
dataMap.put("jobName", "job");
dataMap.put("id", "key");
trigger.setJobDataMap(jobDataMap);
jobDetail = new JobDetailImpl();
jobDetail.setName(getUniqueJobId());
jobDetail.setJobDataMap(jobDataMap);
jobDetail.setJobClass(JobLauncherDetails.class);
scheduler.scheduleJob(jobDetail, trigger);
it is working properly but i want to delete the specific trigger how can i delete
i had seen fee source but those are not matching. please help me out
**Updated : **
i want to delete like
schedule.deleteJob(JobKey) what will be the job key as per my schedule configuration
or should i use schedule.unScheduler()
org.quartz.Scheduler#unscheduleJob accepts the trigger key as parameter. Hence, you can remove the trigger specified, not the job.
public static boolean removeJob(String jobName, String jobGroup) throws SchedulerException {
TriggerKey triggerKey = TriggerKey.triggerKey(jobName, jobGroup);
if (scheduler.checkExists(triggerKey)) {
scheduler.unscheduleJob(triggerKey); // trigger + job
}
logger.info(">>>>>>>>>>> removeJob success, triggerKey:{}", triggerKey);
return true;
}
Hope that helps.
UPDATE:
We don't know actually your functional need. But, you can create Trigger in cleaner way and bind it to a given Job:
CronTrigger trigger = TriggerBuilder.newTrigger()
  .withIdentity("trigger3", "group1")
  .withSchedule(CronScheduleBuilder.cronSchedule("0 0/2 8-17 * * ?"))
  .forJob("myJob", "group1") // Binding the Trigger to the Job
  .build();
I see you are using JobDataMap in your Trigger. This is useful for passing parameters to a Job that are specific to the executions of the trigger. Do you need really that?
Useful link: https://www.baeldung.com/quartz

Running a Java-based Spark Job on spark-jobserver

I need to run an aggregation Spark job using spark-jobserver using low-latency contexts. I have this Scala runner to run a job on using a Java method from a Java class.
object AggregationRunner extends SparkJob {
def main(args: Array[String]) {
val ctx = new SparkContext("local[4]", "spark-jobs")
val config = ConfigFactory.parseString("")
val results = runJob(ctx, config)
}
override def validate(sc: SparkContext, config: Config): SparkJobValidation = {
SparkJobValid;
}
override def runJob(sc: SparkContext, config: Config): Any = {
val context = new JavaSparkContext(sc)
val aggJob = new ServerAggregationJob()
val id = config.getString("input.string").split(" ")(0)
val field = config.getString("input.string").split(" ")(1)
return aggJob.aggregate(context, id, field)
}
}
However, I get the following error. I tried taking out the content returned in the Java method and am now just returning a test string, but it still doesn't work:
{
"status": "ERROR",
"result": {
"message": "Ask timed out on [Actor[akka://JobServer/user/context-supervisor/single-context#1243999360]] after [10000 ms]",
"errorClass": "akka.pattern.AskTimeoutException",
"stack": ["akka.pattern.PromiseActorRef$$anonfun$1.apply$mcV$sp(AskSupport.scala:333)", "akka.actor.Scheduler$$anon$7.run(Scheduler.scala:117)", "scala.concurrent.Future$InternalCallbackExecutor$.scala$concurrent$Future$InternalCallbackExecutor$$unbatchedExecute(Future.scala:694)", "scala.concurrent.Future$InternalCallbackExecutor$.execute(Future.scala:691)", "akka.actor.LightArrayRevolverScheduler$TaskHolder.executeTask(Scheduler.scala:467)", "akka.actor.LightArrayRevolverScheduler$$anon$8.executeBucket$1(Scheduler.scala:419)", "akka.actor.LightArrayRevolverScheduler$$anon$8.nextTick(Scheduler.scala:423)", "akka.actor.LightArrayRevolverScheduler$$anon$8.run(Scheduler.scala:375)", "java.lang.Thread.run(Thread.java:745)"]
}
}
I am not too sure why there is a timeout since I am only returning a string.
EDIT
So I figured out that the issue was occurring because I was using a Spark context that was created before updating a JAR. However, now that I try to use JavaSparkContext inside the Spark job, it returns to the error shown above.
What would be a permanent way to get rid of the error.
Also, would the fact that I am running a heavy Spark job on a local docker container be a plausible reason for the timeout.
For resolving ask time out issue, please add/change below properties in jobserver configuration file.
spray.can.server {
idle-timeout = 210 s
request-timeout = 200 s
}
for more information take a look at this https://github.com/spark-jobserver/spark-jobserver/blob/d1843cbca8e0d07f238cc664709e73bbeea05f2c/doc/troubleshooting.md

Unable to store job because one already exists with this identification

I'm new with Quartz.
I succeeded to install it and run it.
But I have an error when I run it for the second time because the job already exists with this identification.
Here my code :
public void scheduleJobs() throws Exception {
try {
int i = 0;
scheduler = new StdSchedulerFactory().getScheduler();
JobKey job1Key = JobKey.jobKey("job"+i, "my-jobs"+i);
JobDetail job1 = JobBuilder
.newJob(SimpleJob.class)
.withIdentity(job1Key)
.build();
TriggerKey tk1 = TriggerKey.triggerKey("trigger"+i, "my-jobs"+i);
Trigger trigger1 = TriggerBuilder
.newTrigger()
.withIdentity(tk1)
.withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(11, 25))
.build();
scheduler.start(); // start before scheduling jobs
scheduler.scheduleJob(job1, trigger1);
i++;
printJobsAndTriggers(scheduler);
} catch (SchedulerException e) {
LOG.error("Error while creating scheduler", e);
}
}
I tried to use an integer i to change the name but it does not work.
Do you have any idea how can I fix it?
Many thanks.
You can:
check if the "job key" already exists, and remove the existing job before creating a new one:
scheduler.deleteJob(job1Key);
or create a new job with another key (in your case, each time you execute scheduleJobs(), variable i has the same value (0)
or just re-use the same job (why would you create a new job if the old one is still good)
or use the RAM Job Store, which does not persist jobs in database (each time you will use your software, you will have an empty job store)
It really depends on what you want to do with your jobs!
Check for existing job before scheduling:
JobDetail job;
SimpleTrigger trigger;
//Create your trigger and job
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
scheduler.start();
if (scheduler.checkExists(job.getKey())){
scheduler.deleteJob(job.getKey());
}
scheduler.scheduleJob(job, trigger);
This is not a direct answer to the specific code listed in the question, but I didn't notice it when searching elsewhere and thought this might be useful for future readers:
If you're in a situation where you have an existing Job but just want to add a new Trigger, you can call:
scheduler.ScheduleJob(trigger);
and it will add the Trigger to the Job without trying to recreate the Job. The only trick is that you have to make sure the Trigger's JobKey is correct.
My overall code for this interaction looks roughly like:
IJobDetail job; // Handed in
ITrigger trigger; // Handed in
// Keeping track of this because we need to know later whether it's new or not
var newJob = job == null;
if (newJob)
{
job = JobBuilder.Create<TargetJob>()
.WithIdentity([whatever])
[.OtherConfiguration()]
.Build();
}
var trigger = TriggerBuilder
.Create()
.WithIdentity([whatever])
// ** Gotcha #1: Make sure it's linked to the job **
.ForJob(job.Key)
[.OtherConfiguration()]
.Build();
if (newJob)
{
_scheduler.ScheduleJob(job, trigger);
}
else
{
// ** Gotcha #2: Make sure you don't reschedule the job **
_scheduler.ScheduleJob(trigger);
}
If anyone of you are facing the same issue and your solution is in C#. This is how you can fix this error.
This is where we configure the scheduler.
public async Task StartAsync(CancellationToken cancellationToken) {
try {
var scheduler = await GetScheduler();
var serviceProvider = GetConfiguredServiceProvider();
scheduler.JobFactory = new CustomJobFactory(serviceProvider);
await scheduler.Start();
await ConfigureDailyJob(scheduler);
}
catch(Exception ex) {
_logger.Error(new CustomConfigurationException(ex.Message));
}
}
This is how we can configure the Job, please be noted that we are checking whether the job is already there, and if the await scheduler.CheckExists(dailyJob.Key) returns true, we delete that job info and create a new one with the same key.
private async Task ConfigureDailyJob(IScheduler scheduler) {
var dailyJob = GetDailyJob();
if (await scheduler.CheckExists(dailyJob.Key)) {
await scheduler.DeleteJob(dailyJob.Key);
_logger.Info($ "The job key {dailyJob.Key} was already existed, thus deleted the same");
}
await scheduler.ScheduleJob(dailyJob, GetDailyJobTrigger());
}
There are the supporting private functions.
private IJobDetail GetDailyJob() {
return JobBuilder.Create < IDailyJob > ().WithIdentity("dailyjob", "dailygroup").Build();
}
private ITrigger GetDailyJobTrigger() {
return TriggerBuilder.Create().WithIdentity("dailytrigger", "dailygroup").StartNow().WithSimpleSchedule(x = >x.WithIntervalInHours(24).RepeatForever()).Build();
}
You can get the complete source code from this GitHub repository.
You can create new jobs by taking i as a static int. And instead of "job"+i it would be "job"+ Integer.toString(i) . It worked for me.

Passing parameters is not working in Quartz 1.6

I try to pass parameters using quartz(1.6) api in ServletA
JobDataMap map = new JobDataMap();
map.put("date", "20151101");
scheduler = new StdSchedulerFactory().getScheduler();
scheduler.triggerJob(jobName, groupName, map);
When I trigger JobA, it work.
But I can't get parameter in JobA from folloing code.
map is null.
JobDataMap map = jobExecutionContext.getJobDetail().getJobDataMap();
String date = (String) map.get("date");
Do I miss something important?? Thanks for help.
Parameter maps can be passed to the execution from the job or/and the trigger. In your solution you explicitely try to get the job's datamap (at ctx.getJobDetail().getJobDataMap()) , which is null because you set your parameters via the trigger.
You can use the getMergedJobDataMap of JobExecutionContext to get a parameter map merged from both:
JobDataMap map = jobExecutionContext.getMergedJobDataMap();
String date = (String) map.get("date");

java.util.logging custom formatter not working as expected

I am using Java 6. I have created a custom formatter which creates only the time and the message, but it always prints null class name and method name and the log level besides the time and the message in two lines:
Mar 8, 2015 6:48:09 PM null null
ALL: This is a message
Also, the log files generated are in the following format, where I expected it to start with xxx.log.0 first, once it is full, then xxx.log.1, but it generated all 10 files at the same time.
xxxx.log.0.9
xxxx.log.0.8
xxxx.log.0.7
xxxx.log.0.6
xxxx.log.0.5
xxxx.log.0.4
xxxx.log.0.3
Can someone let me know how I can log only the time and the message and how I correct the log file extensions? Much appreciated!
public class MyFormatter extends Formatter {
public String format(LogRecord record)
{
String recordStr = new Date() + " " + formatMessage(record)
return recordStr;
}
}
The code which uses MyFormatter:
Logger fileLogger = Logger.getLogger(xxxx.class.getPackage().getName());
FileHandler handler = new FileHandler(pattern, limit, numLogFiles, true);
MyFormatter formatter = new MyFormatter();
handler.setFormatter(formatter);
fileLogger.addHandler(handler);
Level logLevel = java.util.logging.Level.ALL;
fileLogger.log(new LogRecord(logLevel, "This is a message"));
I am using Java 6. I have created a custom formatter which creates only the time and the message, but it always prints null class name and method name and the log level besides the time and the message in two lines:
That output is from the java.util.logging.SimpleFormatter which has support for printing just the date and message. The following pattern can be used to change the SimpleFormatter to work like your custom formatter:
-Djava.util.logging.SimpleFormatter.format="[%1$tc] %5$s%n"
The code that uses your formatter is not setting the method name, class name, or logger name. Calling the Logger.log(Level,String) method will compute the call site for you.
private static final Logger fileLogger = Logger.getLogger(xxxx.class.getPackage().getName());
private static void initLogging() throws IOException {
FileHandler handler = new FileHandler(pattern, limit, numLogFiles, true);
MyFormatter formatter = new MyFormatter();
handler.setFormatter(formatter);
fileLogger.addHandler(handler);
fileLogger.log(Level.ALL, "This is a message");
}
Not sure if it is intended but, you should use the event time of the LogRecord and not the current time.
Also, the log files generated are in the following format, where I expected it to start with xxx.log.0 first, once it is full, then xxx.log.1, but it generated all 10 files at the same time.
For every FileHandler you create, you have to be sure it is closed by your code before you create a new one with the same pattern. By default the LogManager will close any attached handlers on shutdown. You also have to make sure that you hold a strong reference to the logger that holds the attached handler. Method local references like what is used in your example doesn't count.

Categories

Resources