When I try to schedule two different job with the same cron expression(0 * * * * ?) which is every minute, only one trigger is being fired. I almost tried everything but nothing worked. I also passed the parameter spring.quartz.properties.org.quartz.scheduler.batchTriggerAcquisitionMaxCount=10 but it didn't worked. At least one trigger is firing in a minute and sometimes two, but never two triggers fired in a minute continuously. Am I doing somethig wrong?
edit: I checked on db that Next_Fire_Time is being updated but not being triggered even not misfired or vetoed as I can see from my Trigger Listener.
public class GlobalTriggerListener implements TriggerListener {
private static final String TRIGGER_LISTENER_NAME = "GlobalTriggerListener";
#Override
public String getName() {
return TRIGGER_LISTENER_NAME;
}
#Override
public void triggerFired(Trigger trigger, JobExecutionContext context) {
String triggerName = context.getJobDetail().getKey().toString();
System.out.println("triggerFired");
System.out.println("trigger : " + triggerName + " is fired");
}
#Override
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
boolean veto = false;
System.out.println("Veto Job Excecution trigger: " + veto);
return veto;
}
#Override
public void triggerMisfired(Trigger trigger) {
System.out.println(getName() + " trigger: " + trigger.getKey() + " misfired at " + trigger.getStartTime());
}
#Override
public void triggerComplete(Trigger trigger, JobExecutionContext context, Trigger.CompletedExecutionInstruction triggerInstructionCode) {
System.out.println(getName() + " trigger: " + trigger.getKey() + " completed at " + trigger.getStartTime());
}
}
private JobDetail buildJobDetail(ScheduledProcess sProcess) {
JobDataMap jobDataMap = new JobDataMap();
jobDataMap.put(JOB_DATA_KEY, sProcess.getSpid());
return JobBuilder.newJob(ProcessStarterJob.class)
.withIdentity(UUID.randomUUID().toString(), "scheduled-processes")
.withDescription("Start Process Job")
.usingJobData(jobDataMap)
.storeDurably()
.build();
}
private Trigger buildJobTrigger(JobDetail jobDetail, ScheduledProcess sProcess) throws ParseException {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date startDate = formatter.parse(sProcess.getStartDate());
Date endDate = formatter.parse(sProcess.getEndDate());
return TriggerBuilder.newTrigger()
.withIdentity(jobDetail.getKey().getName(), "scheduled-processes-triggers")
.withDescription("Start Process Trigger")
.startAt(startDate)
.endAt(endDate)
.withSchedule(CronScheduleBuilder.cronSchedule(sProcess.getQuartzDef()).withMisfireHandlingInstructionDoNothing())
.build();
}
Funny but true. I think this the third time I am the victim of Eclipse. Although I couldn't see any instance running on eclipse there was always a stale running instance of my quartz app which I saw it on Task Manager by chance. However I even debugged the quartz source code :)
So if you face with the same problem I have, first of all be sure that there is no running any other instance of your app anywhere, this can also be another quartz app that may be using your quartz db. When I start to be suspicious about if there is any other running instance was the moment I saw that Trigger NEXT_FIRE_TIME was being updated as it has to be but I wasn't seeing any fire on my Trigger Listener.
Hope these help you to identify you problems.
Related
Idea is not to start job if already same job is running.
JobExplorer is simple injected in class where is scheduled method for running
public class JobClass {
private final Job job;
private final JobExplorer jobExplorer;
private final JobLauncher jobLauncher;
public JobMain(Job job,
JobLauncher jobLauncher,
JobExplorer jobExplorer) {
this.job = job;
this.jobLauncher = jobLauncher;
this.jobExplorer = jobExplorer;
}
and then it is executed
#Scheduled("0 */5 * ? * *")
public void startJob() {
JobParameters jobParameters = new JobParametersBuilder()
.addString("jobName", String.valueOf(instant.toEpochMilli()))
.toJobParameters();
jobLauncher.run(job, jobParameters);
}
This is not solution because if JVM stopped while job is running this will be same as current job running:
jobExplorer.findRunningJobExecutions("jobName")
It will find all jobs with exitCode ExitStatus.UNKNOWN.
There is 3 solutions as I see it:
Solution 1:
stop previous running not finished jobs and run new job
PROS: everything is clean, just one property
CONT: loosing current execution of current job
#Scheduled("0 */5 * ? * *")
public void startJob() {
(JobExecution jobExecution: jobExplorer.findRunningJobExecutions("jobName")) jobExecution.stop();
...
}
Solution 2
Calculate time between latest running job like it is here described and if it is any do not start new job:
https://stackoverflow.com/a/23218986/1182625
PROS: everything is clean
CONT: have to have doubled property (5 *60*1000 and "0 */5 * ? * *")
Set<JobExecution> jobExecutions = jobExplorer.findRunningJobExecutions("jobName");
if(jobExecutions.size()>1){
Long currentTime = (new Date()).getTime();
for(JobExecution execution : jobExecutions) {
if((currentTime - execution.getStartTime().getTime()) < 5*60*1000) {
return;
} else {
execution.stop();
}
}
}
Solution 3
Idea is simple to add static (to share between instances of class) volatile (to share between threads) flag which will indicate is any job currently running
PROS: just one property
CONT: needs 2 listeners, and volatile static variable which i don't know how reacted in multi-nodes environment
private static volatile boolean FINISHED = true;
and then simple add listener and FINISHED modify method:
// reset FINISHED after job is done
#AfterJob
public void afterJob() {
FINISHED = true;
}
public void setFinished() {
this.FINISHED = true;
}
And simple add:
#Scheduled("0 */5 * ? * *")
public void startJob() {
if(!FINISHED) return;
FINISHED = false;
...
}
And finally add StepListener
public MyStepListener() {
...
#AfterStep
public ExitStatus afterStep(StepExecution stepExecution) {
if(stepExecution.getExitStatus().getExitCode().equalsIgnoreCase(ExitStatus.FAILED.getExitCode())) (new JobMain()).setFinished();
return null;
}
Ok, I think I go to far with something could be KISS.
Keep It Simple & Stupid.
So, to achieve this is simple to put fixedDelay or fixedStringDelay in #Scheduled annotation if you want to use value from properties file.
#Scheduled(initialDelay = 3*60*1000, fixedDelayString ="${job.fixed_delay}")
With this I achieve that I don't have more than 1 instance of same job at same time.
I only lose that job start at exactly time (like ad midnight or...)
Idea is not to start job if already same job is running.
By design, Spring Batch will prevent that. If you try to start the same job instance while it has a running job execution, you will get a JobExecutionAlreadyRunningException.
There's more than 15 items in my azure database table called Events.
I've tried to run most of the commands found on
https://learn.microsoft.com/en-us/azure/app-service-mobile/app-service-mobile-android-how-to-use-client-library such as :
List<Events> results = eventsTable.execute().get()
and
List<Events> results = eventsTable.select("Events").execute().get();
and
List<Events> results = eventsTable.top(20).execute().get();
to return all the row items in the table. The queries seem to run on the last row of the table only and returns the last row or nothing at all when query is executed.
Though the ToDoItem Quickstart from Azure works perfectly with all the queries - which is odd.
Here's some of the code
ArrayList<Events> events = new ArrayLists<Events>();
private void EventsFromTable() {
AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>(){
#Override
protected Void doInBackground(Void... params) {
try {
final List<Events> results = EventsTable.execute().get();
runOnUiThread(new Runnable() {
#Override
public void run() {
for (Events event : results) {
Events ev = new Events(event.getName(), event.getVenue(), event.getDate());
events.add(ev);
System.out.println("size is " +events.size());
<======This returns "size is 1"======>
}
}
});
} catch (final Exception e){
createAndShowDialogFromTask(e, "Error");
}
return null;
}
};
runAsyncTask(task);
}
Might any one know what the matter is?
Thanks
According to your code, the variable events seems to be a public shared instance of ArraryList in your Android app, so I don't know whether exists the case which multiple threads access it concurrently. The implementation of ArrayList class is not synchronized, please see here.
So please use the code below instead of the code ArrayList<Events> events = new ArrayLists<Events>(); when you shared the variable between UI thread and data async task thread.
List<Events> events = Collections.synchronizedList(new ArrayLists<Events>());
And I think it's better for copying data retrieved from table via addAll method, not add method for each, as the code below.
#Override
public void run() {
events.addAll(results);
}
I am trying to create a sample scheduler program using quartz simple trigger. My objective is run a single method with different parameters passed dynamically by user (considered as new Job)and schedule it for user defined time which is also received dynamically.
eg:
public void printMe (String name, int Age, Date dob) {
system.out.println("Name:"+name+" Age:"+age+" DOB:"+dob);
}
How to schedule and execute this method based on dynamically received parameters and start date and time using quartz 1.6.2. I am new to quartz scheduler, I have no idea how to do this. Do anyone help me with the sample programs you have?
Here is example to use quartz job via simple trigger
Simple Trigger 1 minutes with 1 second delay for every run to execute:
public static void main(String[] args) throws Exception{
JobDetail job = new JobDetail();
job.setName("dummyJobName");
job.setJobClass(HelloJob.class);
//configure the scheduler time
SimpleTrigger trigger = new SimpleTrigger();
trigger.setStartTime(new Date(System.currentTimeMillis() + 1000));
trigger.setRepeatCount(SimpleTrigger.REPEAT_INDEFINITELY);
trigger.setRepeatInterval(100000);// set time run again is 1 minutes
//schedule it
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.start();
scheduler.scheduleJob(job, trigger);
}
You create class for execute class job
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
public class HelloJob implements Job
{
public void execute(JobExecutionContext context)
throws JobExecutionException {
printmMe("YourName",20,"12/15/2013");
}
public void printMe (String name, int Age, Date dob) {
system.out.println("Name:"+name+" Age:"+age+" DOB:"+dob);
}
}
Above, that's program use quartz schedule to execute job HellloClass
I have an Activity A (not the main Activity) that launches a Service S that does some stuff in the background and, in the meanwhile, should made some changes to the UI of A.
Let's just say that S count from 0 to 100 and A should display this count in Real-Time. Since the real job of S is quite more complicated and CPU-consuming, I do not want to use AsyncTask for it (indeed "AsyncTasks should ideally be used for short operations (a few seconds at the most.) [...]") but just a regular Service started in a new Thread (an IntentService would be fine as well).
This is my Activity A:
public class A extends Activity {
private static final String TAG = "Activity";
private TextView countTextView; // TextView that shows the number
Button startButton; // Button to start the count
BResultReceiver resultReceiver;
/**
* Receive the result from the Service B.
*/
class BResultReceiver extends ResultReceiver {
public BResultReceiver(Handler handler) {
super(handler);
}
#Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
switch ( resultCode ) {
case B.RESULT_CODE_COUNT:
String curCount = resultData.getString(B.RESULT_KEY_COUNT);
Log.d(TAG, "ResultReceived: " + curCount + "\n");
runOnUiThread( new UpdateUI(curCount) ); // NOT WORKING AFTER onResume()!!!
break;
}
}
}
/**
* Runnable class to update the UI.
*/
class UpdateUI implements Runnable {
String updateString;
public UpdateUI(String updateString) {
this.updateString = updateString;
}
public void run() {
countTextView.setText(updateString);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.counter);
countTextView = (TextView) findViewById(R.id.countTextView);
startButton = (Button) findViewById(R.id.startButton);
resultReceiver = new BResultReceiver(null);
}
public void startCounting(View view) {
startButton.setEnabled(false);
//Start the B Service:
Intent intent = new Intent(this, B.class);
intent.putExtra("receiver", resultReceiver);
startService(intent);
}
}
And this is my Service B:
public class B extends Service {
private static final String TAG = "Service";
private Looper serviceLooper;
private ServiceHandler serviceHandler;
private ResultReceiver resultReceiver;
private Integer count;
static final int RESULT_CODE_COUNT = 100;
static final String RESULT_KEY_COUNT = "Count";
/**
* Handler that receives messages from the thread.
*/
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
#Override
public void handleMessage(Message msg) {
while ( count < 100 ) {
count++;
//Sleep...
sendMessageToActivity(RESULT_CODE_COUNT, RESULT_KEY_COUNT, count.toString());
}
//Stop the service (using the startId to avoid stopping the service in the middle of handling another job...):
stopSelf(msg.arg1);
}
}
#Override
public void onCreate() {
//Start up the thread running the service:
HandlerThread thread = new HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
this.count = 0;
//Get the HandlerThread's Looper and use it for our Handler
serviceLooper = thread.getLooper();
serviceHandler = new ServiceHandler(serviceLooper);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
this.resultReceiver = intent.getParcelableExtra("receiver");
//For each start request, send a message to start a job and deliver the start ID so we know which request we're stopping when we finish the job:
Message msg = serviceHandler.obtainMessage();
msg.arg1 = startId;
serviceHandler.sendMessage(msg);
//If we get killed, after returning from here, restart:
return START_REDELIVER_INTENT;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* Send a message from to the activity.
*/
protected void sendMessageToActivity(Integer code, String name, String text) {
Bundle bundle = new Bundle();
bundle.putString(name, text);
//Send the message:
resultReceiver.send(code, bundle);
}
}
Everything works fine but if I click the back button (or the home button) and then I re-open the Activity A then the UI of A is not updated anymore (it just shows the initial configuration of A - i.e. the "startButton" is still clickable and the count is not showed - it seems runOnUiThread(...) is not working anymore). However, the Service B is still running in the background and the I can see the correct count is passed to the Activity A in the Log.d(...). Finally, if I click on the "startButton" again, the counting does not start from the beginning (0) but from where B has been arrived (I've double checked it by showing it in the notification bar).
How can I fix this behaviour? I would like that, when I re-open the Activity A, it automatically continues to receive and update the data from the Service B. Or, in other words, that the Service keeps the UI of the Activity A up-to-date.
Please give me some hints, links or piece of code. Thanks!
When you click back button your Activity is destroyed. When you start the Activity again you get a new Activity. That also happen when you rotate the device. This is Android lifecycle event
The Activity is not good for heavy Business Logic only to show stuff/control stuf.
What you have to do is create a simple MVC, Model View Controller. The view (Activity) should only be used for showing results and controlling the eventflow.
The Service can hold an Array of the count and when your Activity start it will onBind() your Service that is running (or if not running will start the Service since you bind to it) Let the Activity(View) get the Array of results and show it. This simple setup exclude the (M)Model Business Logic.
Update
Following up a bit read this it's Android official docs and perfect start since it do kind of what you asking. As you see in the example in the onStart() the Activity establish a connection with the service and in the onStop() the connection is removed. There's no point having a connection after on onStop(). Just like you asking for. I would go with this setup and not let the Service continuously sending data because that would drain resources and the Activity is not always listening because it will stop when in the background.
Here's an activity that binds to LocalService and calls getRandomNumber() when a button is clicked:
I'm working on a basic web client for Quartz.NET that among other things supports the modification of a job's JobDataMap at runtime.
My job is decorated with the following attributes which I believe is all that is necessary to make the job stateful:
[PersistJobDataAfterExecution]
[DisallowConcurrentExecution]
public class SampleTenantJob : IJob { ... }
At runtime, I execute the following code but the JobDataMap is not persisted:
public void UpdateJobProperties(string jobName, string groupName, IDictionary<string, object> properties)
{
IJobDetail job;
if (!TryGetJob(jobName, groupName, out job))
return;
foreach (var property in properties)
{
if (job.JobDataMap.ContainsKey(property.Key) && property.Value != null)
{
job.JobDataMap.Put(property.Key, property.Value);
}
}
}
I thought initially this was because I was using the XMLSchedulingDataProcessorPlugin for jobs but I've tried both the in memory (RAMStore) and AdoJobStore and still can not persist JobDataMap changes made by the remote client.
PersistJobDataAfterExecution (as the name implies) only applies when the job has finished executing, so the following job will track the number of times it is executed
[PersistJobDataAfterExecution]
public class HelloJob : IJob
{
public void Execute(IJobExecutionContext context)
{
int count = (int?) context.MergedJobDataMap["Count"] ?? 1;
Console.WriteLine("hello " + count.ToString() );
context.JobDetail.JobDataMap.Put("Count" , ++count);
}
}
Without the PersistJobDataAfterExecution attributes, count is always the same.
Since you aren't running the job, this doesn't help you, and I think you have to delete and re-create the job with the new JobDataMap.
Of course, you aren't forced to use JobDataMap and can always to read and store information for your job from somewhere else.