How to cancel an already scheduled TimerTask? - java

I have a tiny problem that I can't seem to do right. I have the following class in java:
package pooledtcpconnector.utilities;
import java.io.IOException;
import java.io.InputStream;
import java.util.Timer;
import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;
public final class Notifier implements Runnable {
private final ILogger logger;
private Timer mTimer;
private final int Treshold;
private final InputStream ResponseStream;
private final TimerTask DoWaitTask;
public Notifier(final InputStream _ResponseStream, final Integer _Treshold, final ILogger logger) {
this.logger = logger;
mTimer = new Timer();
this.ResponseStream = _ResponseStream;
this.Treshold = _Treshold;
DoWaitTask = new TimerTask() {
#Override
public void run() {
try {
int mSize = ResponseStream.available();
if (mSize >= Treshold) {
mTimer.cancel();
}
} catch (final IOException ex) {
final String ExceptionMessage = ex.getMessage();
logger.LogMessage(
this.getClass().getCanonicalName(),
"Notifier.DoWaitTask:run.ResponseStream.available",
ex.getClass().getCanonicalName(),
new String[]{
ExceptionMessage,
"Parameters:",
String.format("-"),
});
Logger.getLogger(Notifier.class.getCanonicalName()).log(Level.FINE, ex.getMessage(), ex.getCause());
}
}
};
}
#Override
public void run() {
synchronized (this) {
mTimer.scheduleAtFixedRate(DoWaitTask, 250, 200);
// Notification mechanism
notify();
}
}
}
This class would ensure that our application won't start processing the SocketInputStream unless the available method returns at least Treshold. The problem however is that, once I schedule the DoWaitTask with the Timer it runs for eternity. By cancelling the timer the task still runs and the whole application hangs, but more importantly it tries to call available on the stream once it already has been processed and closed. Of course this results in a nice IOException: stream closed.
How could I stop the scheduled task along with the timer? timer.cancel obviously isn't enough.
Regards,
Joey

Use TimerTask.cancel() from within your timer task's run() method. According to the Javadoc for this method:
Note that calling this method from within the run method of a
repeating timer task absolutely guarantees that the timer task will
not run again.

private Timer reportTimer = null;
if (reportTimer != null) {
reportTimer.cancel();
reportTimer = null;
}
reportTimer = new Timer();
reportTimer.schedule(new TimerTask() {}

Related

Thread Executing sometimes doubled

I have an old application that has a robot thread that executes everyday. I have the source for the robot, but I don't know how this tread is started. And there's a log, witch includes a line in a database, that sometimes includes 2 identical lines, proving that the process is executing doubled.
we use Windows Server 2003
public void run()
{
while (true)
{
starter();
try {
Thread.sleep(10800000L);
}
catch (InterruptedException localInterruptedException)
{
}
}
}
I need to keep it from executing more than once.
I'm new to treads, don't really get the works of a thread right yet...
thank you all in advance...
You don't really provide a lot of information, but here are a few guesses:
Could the entire process be launched twice?
If the thread is running twice, showing the thread run function won't help. You'll have to find the code that creates the thread.
Log something in the catch block. Maybe a sleep call gets interrupted immediately and this is why starter() is called twice at almost the same time.
public void run()
{
int delay = 86400000; //milliseconds
ActionListener taskPerformer = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
starter();
}
};
new Timer(delay, taskPerformer).start();
}
Or...
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class DailyTask extends TimerTask {
#Override
public void run() {
//Or if you use a logger like log4j you can insert logger code here.
System.out.println("Start:" + new Date());
starter();
System.out.println("End:" + new Date());
}
public static void main(String args[]) {
TimerTask tt= new DailyTask();
// running timer task as daemon thread
Timer t = new Timer(true);
t.scheduleAtFixedRate(tt, 0, 86400000);
System.out.println("DailyTask started:" + new Date());
}
}
This should run starter() once every 24 hours.

Returning a data-type from a thread

Basically, I'm doing some SQL Querying on a remote database, and I've been told to always run queries in a seperate thread to not bother the loop of the main application (Considering it's a real time game). I'm just getting around to adding SQL support and not running the queries on a seperate thread causes massive lag; Here's what I'm attempting to do:
public boolean login(final String username, final String password) {
final AtomicBoolean value = new AtomicBoolean(false);
Thread thread = new Thread(new Runnable() {
public void run() {
Connection connection = null;
Statement statement = null;
ResultSet results = null;
try {
connection = getConnection("root", "", "localhost");
statement = connection.createStatement();
results = statement.executeQuery("SELECT * from `db`.`accounts` WHERE `username`='"+username+"'");
while(results.next()) {
String salt = results.getString("salt");
String dbPass = results.getString("password");
String hashPass = toMD5(toMD5(salt) + toMD5(password));
if(hashPass.equals(dbPass)) {
value.set(true);
}
}
} catch(SQLException e) {
e.printStackTrace();
}
}
});
thread.start();
return value.get();
}
However, the problem is that the value of the atomic boolean is never set before the application is returned. I'm trying to find the best way to do this without blocking the thread that I'm calling on.
Note: Well aware I should be using prepared statements here, trying to figure this out first.
I'm trying to find the best way to do this without blocking the thread that I'm calling on.
That is impossible. If that thread needs the result, it will have to block until that result is available.
One option is to use a CompletableFuture<Boolean>.
CompletableFuture<Boolean> future = new CompletableFuture<>();
Thread thread = new Thread(new Runnable() {
public void run() {
...
// when ready, successful (maybe false otherwise)
future.complete(true)
...
}
});
You can then either call future.get() which blocks the current thread or you can register a listener which will be invoked when the result is set (in that other thread, or in the current thread if the result is already ready).
Instead of managing your own thread, use a thread pool.
I'm trying to find the best way to do this without blocking the thread that I'm calling on.
What you need is a listener, that can listen for your thread to finish and then execute your code.
Let's assume that you want to return a ResultSet from your thread. First, you create your own EventObject.
package com.ggl.event.listener;
import java.sql.ResultSet;
import java.util.EventObject;
public class ResultSetEventObject extends EventObject {
private static final long serialVersionUID = 6904165475135245131L;
private ResultSet resultSet;
public ResultSetEventObject(Object source) {
super(source);
}
public ResultSet getResultSet() {
return resultSet;
}
public void setResultSet(ResultSet resultSet) {
this.resultSet = resultSet;
}
}
There’s nothing special about this class, other than it extends EventObject. Your constructor is defined by EventObject, but you can create any methods you want.
Second, you define an EventListener interface.
package com.ggl.event.listener;
public interface EventListener {
public void handleEvent(ResultSetEventObject eo);
}
You would use the EventObject you created. You can use any method name or names that you want. This is the interface for the code that will be written as a response to the listener.
Third, you write a ListenerHandler.
package com.ggl.event.listener;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
public class ResultSetListenerHandler {
protected List<EventListener> listeners;
public ResultSetListenerHandler() {
listeners = new ArrayList<EventListener>();
}
// Add method(s) to generate a ResultSet and perform the
// fireEvent method.
public void addListener(EventListener listener) {
listeners.add(listener);
}
public void removeListener(EventListener listener) {
for (int i = listeners.size() - 1; i >= 0; i--) {
EventListener instance = listeners.get(i);
if (instance.equals(listener)) {
listeners.remove(i);
}
}
}
public void fireEvent(final ResultSetEventObject eo,
final ResultSet resultSet) {
for (int i = 0; i < listeners.size(); i++) {
final EventListener instance = listeners.get(i);
Runnable runnable = new Runnable() {
public void run() {
eo.setResultSet(resultSet);
instance.handleEvent(eo);
}
};
new Thread(runnable).start();
}
}
}
And that's how you write an event listener. In your code, you would write something like this:
ResultSetListenerHandler handler = new ResultSetListenerHandler();
handler.addListener(new EventListener() {
#Override
public void handleEvent(ResultSetEventObject eo) {
// Code to process the eo ResultSet goes here
}
});

Code not executed without a print statement [duplicate]

This question already has an answer here:
Loop doesn't see value changed by other thread without a print statement
(1 answer)
Closed 7 years ago.
i've been making a countdown program, and i came up with this.
package main;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.Clip;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.UnsupportedAudioFileException;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
public class Gatoo extends JFrame implements ActionListener {
private int sec, min, secTot, since = 999;
private long lastTime;
private JTextField mm = new JTextField(2), ss = new JTextField(2);
private JLabel minLab = new JLabel("Minutes:"), secLab = new JLabel(
"Seconds:");
private JButton start = new JButton("Start");
private Clip done;
private boolean started = false;
private static final long serialVersionUID = 4277921337939922028L;
public static void main(String[] args) {
Gatoo cake = new Gatoo("Title");
cake.pack();
cake.setSize(800, 600);
cake.setLocationRelativeTo(null);
cake.setDefaultCloseOperation(3);
cake.setVisible(true);
cake.run();
}
public Gatoo(String s) {
super(s);
setLayout(new FlowLayout());
start.addActionListener(this);
add(minLab);
add(mm);
add(secLab);
add(ss);
add(start);
}
#Override
public void actionPerformed(ActionEvent e) {
started = true;
}
public void play(File file) throws MalformedURLException,
UnsupportedAudioFileException, IOException,
LineUnavailableException {
AudioInputStream ais = AudioSystem.getAudioInputStream(new File(
"lib/done.wav"));
DataLine.Info info = new DataLine.Info(Clip.class, ais.getFormat());
done = (Clip) AudioSystem.getLine(info);
done.open(ais);
done.start();
}
public void run() {
while (true) {
System.out.print("");// needed?
if (started) {
try {
min = Integer.parseInt(mm.getText());
sec = Integer.parseInt(ss.getText());
secTot = (min * 60) + sec;
lastTime = System.currentTimeMillis();
while (secTot > 0) {
since = (int) (System.currentTimeMillis() - lastTime);
if (since > 998) {
lastTime = System.currentTimeMillis();
secTot--;
}
}
play(new File("done.wav"));
} catch (NumberFormatException exception) {
System.out.println("Minutes and seconds must be numbers.");
return;
} catch (Exception exception) {
exception.printStackTrace();
}
started = false;
}
}
}
}
In the while loop at the end the countdown code doesn't execute without a print / println statement inside. How come? The program works perfectly fine with the print statement though.
First and foremost, your program is thread-unsafe because boolean started is a shared variable, but it is neither volatile nor accessed within synchronized blocks.
Now, accidentally, PrintStream#print is a synchronized method and, on any actual architecture, entering and exiting a synchronized block is implemented using memory barrier CPU instructions, which cause a complete synchronization between the thread-local state and main memory.
Therefore, by pure accident, adding the print call allows the setting of started flag by one thread (the EDT) to be visible by another (the main thread).
You have poor design for Swing application.
Don't use while(true) loop in your run() method. Read more about Concurency in Swing.
Call events with help of Listeners(ActionListener e.g.) instead of flags(started here).
Instead of counting time use Swing Timer.
Change your run() method like next:
public void run() {
min = Integer.parseInt(mm.getText());
sec = Integer.parseInt(ss.getText());
secTot = (min * 60) + sec;
Timer timer = new Timer(1000*secTot, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
try {
play(new File("done.wav"));
} catch (Exception e1) {
e1.printStackTrace();
}
}
});
timer.start();
}
actionPerformed() method :
#Override
public void actionPerformed(ActionEvent e) {
run();
}
and remove cake.run() in main method.
Look, I made a SSCCE reproducing this behavior. It is a really good question.
public class ThreadRacing implements Runnable
{
public boolean started = false;
public static void main(String[] args)
{
new ThreadRacing().test();
}
public void test()
{
new Thread(this).start();
try
{
Thread.sleep(1000);
} catch (Exception e)
{
}
started = true;
System.out.println("I did my job");
}
#Override
public void run()
{
while (true)
{
//System.out.print("");
if (started)
{
System.out.println("I started!!");
}
}
}
}
This prints: "I did my job". Nothing more. Adding a volatile keyword actually fixes the problem.
To me, it looks like the second Thread gets not notified about the update to started because he is too bussy.
I would surmise that your busy-wait loop is hogging the CPU so severely it is unable to do anything. The print statement is causing just enough of a thread context switch that it is able to get other work done.
Edit: Okay, I did a little testing. I was able to reproduce OP's problem on the HotSpot Server VM. Using Thread.currentThread().setPriority(Thread.MIN_PRIORITY); did not fix it, so it is not a starvation issue. Setting the variable to volatile as #MartinCourteau, #MarkoTopolnik suggested, did fix it. That makes sense. I couldn't originally reproduce the problem on the HotSpot Client VM; apparently its optimizations are too weak for it to cache the started variable.
(Still, if the Java audio thread had a lower than normal thread priority and it were a single-CPU system, starvation was a plausible hypothesis.)

Executor/Queue process last known task only

I'm looking to write some concurrent code which will process an event. This processing can take a long time.
Whilst that event is processing it should record incoming events and then process the last incoming events when it is free to run again. (The other events can be thrown away). This is a little bit like a FILO queue but I only need to store one element in the queue.
Ideally I would like to plug in my new Executor into my event processing architecture shown below.
public class AsyncNode<I, O> extends AbstractNode<I, O> {
private static final Logger log = LoggerFactory.getLogger(AsyncNode.class);
private Executor executor;
public AsyncNode(EventHandler<I, O> handler, Executor executor) {
super(handler);
this.executor = executor;
}
#Override
public void emit(O output) {
if (output != null) {
for (EventListener<O> node : children) {
node.handle(output);
}
}
}
#Override
public void handle(final I input) {
executor.execute(new Runnable() {
#Override
public void run() {
try{
emit(handler.process(input));
}catch (Exception e){
log.error("Exception occured whilst processing input." ,e);
throw e;
}
}
});
}
}
I wouldn't do either. I would have an AtomicReference to the event you want to process and add a task to process it in a destructive way.
final AtomicReference<Event> eventRef =
public void processEvent(Event event) {
eventRef.set(event);
executor.submit(new Runnable() {
public vodi run() {
Event e = eventRef.getAndSet(null);
if (e == null) return;
// process event
}
}
}
This will only ever process the next event when the executor is free, without customising the executor or queue (which can be used for other things)
This also scales to having keyed events i.e. you want to process the last event for a key.
I think the key to this is the "discard policy" you need to apply to your Executor. If you only want to handle the latest task then you need a queue size of one and a "discarding policy" of throw away the oldest. Here is an example of an Executor that will do this
Executor latestTaskExecutor = new ThreadPoolExecutor(1, 1, // Single threaded
30L, TimeUnit.SECONDS, // Keep alive, not really important here
new ArrayBlockingQueue<>(1), // Single element queue
new ThreadPoolExecutor.DiscardOldestPolicy()); // When new work is submitted discard oldest
Then when your tasks come in just submit them to this executor, if there is already a queued job it will be replaced with the new one
latestTaskExecutor.execute(() -> doUpdate()));
Here is a example app showing this working
import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class LatestUpdate {
private static final Executor latestTaskExecutor = new ThreadPoolExecutor(1, 1, // Single threaded
30L, TimeUnit.SECONDS, // Keep alive, not really important here
new ArrayBlockingQueue<>(1), // Single element queue
new ThreadPoolExecutor.DiscardOldestPolicy()); // When new work is submitted discard oldest
private static final AtomicInteger counter = new AtomicInteger(0);
private static final Random random = new Random();
public static void main(String[] args) {
LatestUpdate latestUpdate = new LatestUpdate();
latestUpdate.run();
}
private void doUpdate(int number) {
System.out.println("Latest number updated is: " + number);
try { // Wait a random amount of time up to 5 seconds. Processing the update takes time...
Thread.sleep(random.nextInt(5000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private void run() {
// Updates a counter every second and schedules an update event
Thread counterUpdater = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
Thread.sleep(1000L); // Wait one second
} catch (InterruptedException e) {
e.printStackTrace();
}
counter.incrementAndGet();
// Schedule this update will replace any existing update waiting
latestTaskExecutor.execute(() -> doUpdate(counter.get()));
System.out.println("New number is: " + counter.get());
}
});
counterUpdater.start(); // Run the thread
}
}
This also covers the case for GUIs where once updates stop arriving you want the GUI to become eventually consistent with the last event received.
public class LatestTaskExecutor implements Executor {
private final AtomicReference<Runnable> lastTask =new AtomicReference<>();
private final Executor executor;
public LatestTaskExecutor(Executor executor) {
super();
this.executor = executor;
}
#Override
public void execute(Runnable command) {
lastTask.set(command);
executor.execute(new Runnable() {
#Override
public void run() {
Runnable task=lastTask.getAndSet(null);
if(task!=null){
task.run();
}
}
});
}
}
#RunWith( MockitoJUnitRunner.class )
public class LatestTaskExecutorTest {
#Mock private Executor executor;
private LatestTaskExecutor latestExecutor;
#Before
public void setup(){
latestExecutor=new LatestTaskExecutor(executor);
}
#Test
public void testRunSingleTask() {
Runnable run=mock(Runnable.class);
latestExecutor.execute(run);
ArgumentCaptor<Runnable> captor=ArgumentCaptor.forClass(Runnable.class);
verify(executor).execute(captor.capture());
captor.getValue().run();
verify(run).run();
}
#Test
public void discardsIntermediateUpdates(){
Runnable run=mock(Runnable.class);
Runnable run2=mock(Runnable.class);
latestExecutor.execute(run);
latestExecutor.execute(run2);
ArgumentCaptor<Runnable> captor=ArgumentCaptor.forClass(Runnable.class);
verify(executor,times(2)).execute(captor.capture());
for (Runnable runnable:captor.getAllValues()){
runnable.run();
}
verify(run2).run();
verifyNoMoreInteractions(run);
}
}
This answer is a modified version of the one from DD which minimzes submission of superfluous tasks.
An atomic reference is used to keep track of the latest event. A custom task is submitted to the queue for potentially processing an event, only the task that gets to read the latest event actually goes ahead and does useful work before clearing out the atomic reference to null. When other tasks get a chance to run and find no event is available to process, they just do nothing and pass away silently. Submitting superfluous tasks are avoided by tracking the number of available tasks in the queue. If there is at least one task pending in the queue, we can avoid submitting the task as the event will be handled when an already queued task is dequeued.
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class EventExecutorService implements Executor {
private final Executor executor;
// the field which keeps track of the latest available event to process
private final AtomicReference<Runnable> latestEventReference = new AtomicReference<>();
private final AtomicInteger activeTaskCount = new AtomicInteger(0);
public EventExecutorService(final Executor executor) {
this.executor = executor;
}
#Override
public void execute(final Runnable eventTask) {
// update the latest event
latestEventReference.set(eventTask);
// read count _after_ updating event
final int activeTasks = activeTaskCount.get();
if (activeTasks == 0) {
// there is definitely no other task to process this event, create a new task
final Runnable customTask = new Runnable() {
#Override
public void run() {
// decrement the count for available tasks _before_ reading event
activeTaskCount.decrementAndGet();
// find the latest available event to process
final Runnable currentTask = latestEventReference.getAndSet(null);
if (currentTask != null) {
// if such an event exists, process it
currentTask.run();
} else {
// somebody stole away the latest event. Do nothing.
}
}
};
// increment tasks count _before_ submitting task
activeTaskCount.incrementAndGet();
// submit the new task to the queue for processing
executor.execute(customTask);
}
}
}
Though I like James Mudd's solution but it still enqueues a second task while previous is running which might be undesirable. If you want to always ignore/discard arriving task if previous is not completed you can make some wrapper like this:
public class DiscardingSubmitter {
private final ExecutorService es = Executors.newSingleThreadExecutor();
private Future<?> future = CompletableFuture.completedFuture(null); //to avoid null check
public void submit(Runnable r){
if (future.isDone()) {
future = es.submit(r);
}else {
//Task skipped, log if you want
}
}
}

Quartz Jobs Not Kicking Off

Edit: I am using quartz-2.1.5.jar. Here's the summary of my classes:
HttpPollingJob extends PollingJob extends ScheduledJob implements org.quartz.Job
Specifically:
1) ScheduledJob implements Quartz Job (abstract base class for all my Job types):
import org.quartz.Job;
import org.quartz.Trigger;
public abstract class ScheduledJob implements Job {
private Trigger trigger;
public ScheduledJob() {
this(null);
}
public ScheduledJob(Trigger trig) {
super();
if(trig == null)
trig = getDefaultTrigger();
setTrigger(trig);
}
public Trigger getTrigger() {
return trigger;
}
public void setTrigger(final Trigger trig) {
trigger = trig;
}
protected abstract Trigger getDefaultTrigger();
}
2) PollingJob extends ScheduledJob - all "pollers" poll some resource/endpoint with a specific frequency:
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import com.me.jobs.ScheduledJob;
public abstract class PollingJob extends ScheduledJob {
private static long DEF_FREQUENCY = 10 * 1000; // 10 secs
private String name;
private long frequency;
public PollingJob(final String nm) {
this(nm, DEF_FREQUENCY);
}
public PollingJob(final String nm, final long freq) {
super();
setName(nm);
setFrequency(freq);
}
public abstract void poll(JobExecutionContext context);
#Override
public void execute(JobExecutionContext context) throws JobExecutionException {
poll(context);
}
public String getName() {
return name;
}
public void setName(final String nm) {
name = nm;
}
public long getFrequency() {
return frequency;
}
public void setFrequency(final long freq) {
frequency = freq;
}
protected final Trigger getDefaultTrigger() {
TriggerBuilder<?> triggerBuilder = TriggerBuilder.newTrigger()
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInMilliseconds(DEF_FREQUENCY));
return triggerBuilder.build();
}
}
3) HttpPollingJob extends PollingJob - "HTTP pollers" poll a web server (using HTtpClient):
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.quartz.JobExecutionContext;
import com.me.MonitoredEvent;
import com.me.MonitoredEventRegistrar;
public class HttpPollingJob extends PollingJob {
private String serverURL;
private org.slf4j.Logger logger =
org.slf4j.LoggerFactory.getLogger(HttpPollingJob.class);
public HttpPollingJob(final String nm, final String server) {
super(nm);
setServerURL(server);
}
public String getServerURL() {
return serverURL;
}
public void setServerURL(final String server) {
serverURL = server;
}
#Override
public final void poll(JobExecutionContext context) {
MonitoredEvent event = null;
try {
// This is where we would use HttpClient to connect to a web server and poll it.
System.out.println("Job fired!");
}
catch(Throwable thrown) {
logger.error(thrown.getMessage());
}
}
}
4) JobDriver - defines several HttpPollingJobs and uses Quartz to start them:
public class JobDriver {
private List<HttpPollingJob> jobs;
public JobDriver() {
HttpPollingJob job1 = new HttpPollingJob("job-1", "http://www.example.com/1");
HttpPollingJob job2 = new HttpPollingJob("job-2", "http://www.example.com/2");
HttpPollingJob job3 = new HttpPollingJob("job-3", "http://www.example.com/3");
jobs = new ArrayList<HttpPollingJob>();
jobs.add(job1);
jobs.add(job2);
jobs.add(job3);
}
public static void main(String[] args) {
JobDriver driver = new JobDriver();
driver.startJobs();
}
private void startJobs() {
try {
// Obtain a basic SchedulerFactory and fire it up.
SchedulerFactory schedulerFactory = new org.quartz.impl.StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.start();
// Define a job for every declared monitor.
JobBuilder jobBuilder = null;
for(ScheduledJob job : jobs) {
Trigger trigger = job.getTrigger();
jobBuilder = JobBuilder.newJob(job.getClass());
// Bind the current job to this trigger.
scheduler.scheduleJob(jobBuilder.build(), trigger);
// TODO: Shut the scheduler down politely?!?!
}
catch(Throwable exc) {
logger.error(exc.getMessage());
// Force application to kick out.
throw new RuntimeException(exc);
}
}
}
When I run this code I get a perfect startup with no errors or runtime exceptions. If I sprinkle System.out.println statements I can see every single line of code executing flawlessly. The only problem is, once the program is running, its not printing the "Job fired!" message indicating that the polling job is kicking off.
I've tried every combination of start() and shutdown() I can think of to no avail. Can any Quartz mavens look at this code and tell me why the job isn't firing?
In the logs (I configured log4j) I see the Quartz worker thread being created for the scheduled job. I feel like I'm 99% of the way there but am just missing something obvious. Thanks in advance!
#4herpsand7derpsago yes you are right, you are 99% there, I ran your code and there is only one problem in it, the Default constructors are absent in HttpPollingJob and PollingJob classes, For that reason Scheduler is not able to create their instances,
Simple Solution add following code in below classes
HttpPollingJob class
public HttpPollingJob() {
}
PollingJob class
public PollingJob() {
}
Bingo, the following messages will be printed
Job fired!
Job fired!
Job fired!
If you want to repeat the trigger, add following code in PollingJob
protected final Trigger getDefaultTrigger() {
TriggerBuilder<?> triggerBuilder = TriggerBuilder
.newTrigger()
.startNow()
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
.withIntervalInMilliseconds(DEF_FREQUENCY).repeatForever());
return triggerBuilder.build();
}
Hoping that now I will receive the bounty :)
BONUS
Seems like you want to poll or do something with urls, A better way to pass that using king JobDataMap
Updated JobDriver
import java.util.ArrayList;
import java.util.List;
import org.quartz.JobBuilder;
import org.quartz.Scheduler;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
public class JobDriver {
private List<HttpPollingJob> jobs;
public JobDriver() {
HttpPollingJob job1 = new HttpPollingJob("job-1",
"http://www.example.com/1");
HttpPollingJob job2 = new HttpPollingJob("job-2",
"http://www.example.com/2");
HttpPollingJob job3 = new HttpPollingJob("job-3",
"http://www.example.com/3");
jobs = new ArrayList<HttpPollingJob>();
jobs.add(job1);
jobs.add(job2);
jobs.add(job3);
}
public static void main(String[] args) {
JobDriver driver = new JobDriver();
driver.startJobs();
}
private void startJobs() {
try {
// Obtain a basic SchedulerFactory and fire it up.
SchedulerFactory schedulerFactory = new org.quartz.impl.StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
scheduler.start();
// Define a job for every declared monitor.
JobBuilder jobBuilder = null;
for (HttpPollingJob job : jobs) {
Trigger trigger = job.getTrigger();
jobBuilder = JobBuilder.newJob(job.getClass());
jobBuilder.usingJobData("name", job.getName());
jobBuilder.usingJobData("url", job.getServerURL());
// Bind the current job to this trigger.
scheduler.scheduleJob(jobBuilder.build(), trigger);
// TODO: Shut the scheduler down politely?!?!
}
} catch (Throwable exc) {
// Force application to kick out.
throw new RuntimeException(exc);
}
}
}
updated HttpPollingJob
import java.util.Map;
import org.quartz.JobExecutionContext;
public class HttpPollingJob extends PollingJob {
private String serverURL;
private org.slf4j.Logger logger =
org.slf4j.LoggerFactory.getLogger(HttpPollingJob.class);
public HttpPollingJob(final String nm, final String server) {
super(nm);
setServerURL(server);
}
public HttpPollingJob() {
}
public String getServerURL() {
return serverURL;
}
public void setServerURL(final String server) {
serverURL = server;
}
#Override
public final void poll(JobExecutionContext context) {
try {
Map dataMap = context.getJobDetail().getJobDataMap();
String nm = (String)dataMap.get("name");
String url = (String)dataMap.get("url");
// This is where we would use HttpClient to connect to a web server and poll it.
System.out.println("Job fired! name:"+nm+" url:"+url);
}
catch(Throwable thrown) {
logger.error(thrown.getMessage());
}
}
}
new output
Job fired! name:job-1 url:http://www.example.com/1
Job fired! name:job-2 url:http://www.example.com/2
Job fired! name:job-3 url:http://www.example.com/3
Apparently, you have not started the trigger. See Quartz Tutorial or Javadocs:
// Trigger the job to run now, and then every 40 seconds
Trigger trigger = newTrigger()
.withIdentity("myTrigger", "group1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(40)
.repeatForever())
.build();
Its your constructors - the JobBuilder looks for the no-args and since they're not defined it refuses to build the jobs. Add empty no-arg ctors and you're all set.
Could you try to change the code of the method getDefaultTrigger in the class PollingJob into the following :
protected final Trigger getDefaultTrigger() {
return TriggerBuilder.newTrigger()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInMilliseconds(DEF_FREQUENCY))
.startAt(DateBuilder.futureDate(1, DateBuilder.IntervalUnit.SECOND))
.build();
}
i feel some issue with the way you create the trigger.
Remember you cannot reuse trigger for multiple jobs
Try creating trigger with unique identity and group like below
Trigger everyHourTrigger = newTrigger().withIdentity("everyWeeklyTrigger", "group1")
.startNow().withSchedule(cronSchedule("0 1 * * * ?")).build();

Categories

Resources