How to wait cancellation of task after Service#cancel? - javafx

Look at this example:
package sample;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.stage.Stage;
public class Main extends Application {
//NOTICE: This is class in **other file** (here is just for example)
private static class MyService extends Service {
#Override
protected Task createTask() {
return new Task() {
#Override
protected Object call() throws Exception {
System.out.println("Service: START");
while(true) {
System.out.println("Service: ITERATION");
// Thread.sleep(3000); // This raise InterruptedException after cancel, but how about such code (it won't raise exception):
for(long i = 0; i < 1_000_000_000; i++) {
}
if (isCancelled())
break;
}
System.out.println("Service: END");
return null;
}
};
}
}
#Override
public void start(Stage primaryStage) throws Exception {
MyService myService = new MyService();
myService.start();
Thread.sleep(5000);
myService.cancel();
System.out.println(myService.getState()); // Here is `CANCELLED` already but task isn't finished yet.
// <--- How to wait cancellation of Task here?
System.out.println("This command must be called after `Service: END`");
Platform.exit();
}
public static void main(String[] args) {
launch(args);
}
}
As you known call of Service#cancel doesn't wait cancellation of Task. So, I want to block main thread and await cancellation of Task. How can I do it?
P.S.
Looks like Service doesn't provide any callback/event handler to check real cancellation of Task. Is it right?

By default, Service.cancel() interrupts the Task. So an InterruptedException must be raised and your task will be terminated (forcefully).
One thing you could do is to store the created task in a global variable in your MyService class and override the cancel method like this:
class MyService extends Service {
private Task t;
#Override
public boolean cancel() {
if (t != null) {
return t.cancel(false);
} else {
return false;
}
}
#Override
protected Task createTask() {
t = new Task() { /* ... */ };
return t;
}
}
The rest will be easy. Add a change listener to the service state property (or use setOnCanceled() method) and do whatever you want to do after the state change, in the callback.

Never block the FX Application Thread.
The Service class does indeed define a setOnCancelled(...) method, which you use to register a callback:
myService.setOnCancelled(event -> {
System.out.println("Service was cancelled");
});
Note that when you cancel a Service, it will interrupt the thread if it is blocked. So if you don't catch the InterruptedException it will not exit the call method normally. This is why you don't see the "END" message.
Full example code:
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ServiceCancellationTest extends Application {
//NOTICE: This is class in **other file** (here is just for example)
private static class MyService extends Service<Void> {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
System.out.println("Service: START");
while(! isCancelled()) {
System.out.println("Service: ITERATION");
try {
Thread.sleep(3000);
} catch (InterruptedException interrupted) {
System.out.println("Task interrupted");
}
if (isCancelled())
break;
}
System.out.println("Service: END");
return null;
}
};
}
}
#Override
public void start(Stage primaryStage) throws Exception {
MyService myService = new MyService();
myService.start();
myService.setOnCancelled(event -> {
System.out.println("In cancelled callback: "+myService.getState()); // Here is `CANCELLED` already but task isn't finished yet.
});
// You should never block the FX Application Thread. To effect a pause,
// use a pause transition and execute the code you want in its
// onFinished handler:
PauseTransition pause = new PauseTransition(Duration.seconds(5));
pause.setOnFinished(event -> {
myService.cancel();
System.out.println("After calling cancel: "+myService.getState());
System.out.println("This command must be called after `Service: END`");
Platform.exit();
});
pause.play();
}
public static void main(String[] args) {
launch(args);
}
}

Related

JavaFx: about quartz scheduler

Hello I have a problem in a routine using quartz scheduler I need to shutdown my Scheduler method:
javafx stop
I can't declare my scheduler out of my stage start:
#Override
public void start(Stage stage) throws Exception {
Scheduler s = StdSchedulerFactory.getDefaultScheduler();
JobDetail j = JobBuilder.newJob(ChecarJob.class).build();
Trigger t = TriggerBuilder.newTrigger().withIdentity("CroneTrigger")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(60).repeatForever()).build();
try {
s.start();
try {
s.scheduleJob(j,t);
} catch (Exception e) {
e.printStackTrace();
}
} catch (SchedulerException se) {
se.printStackTrace();
}
Parent root = FXMLLoader.load(getClass().getResource("/fxml/Principal.fxml")); //carrega fxml
Scene scene = new Scene(root); //coloca o fxml em uma cena
stage.setScene(scene); // coloca a cena em uma janela
stage.show(); //abre a janela
setStage(stage);
}
and I need to declare it was outside my start to be able to use shutdown inside stop ()
#Override
public void stop() {
UsuarioDAO u = new UsuarioDAO();
u.setOffiline();
s.shutdown();
Platform.exit();
System.exit(0);
}
if I do that I did above I have an error because my Scheduler was created inside a method and is not global
And the scheduler doesn't allow me to create it at global scope for some reason
my code:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package views;
import dao.UsuarioDAO;
import dao.qtdRegistrosDAO;
import rotinas.BackupJob;
import rotinas.ChecarJob;
import java.io.IOException;
import java.net.ServerSocket;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javax.swing.JOptionPane;
import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;
/**
* FXML Controller class
*
* #author SpiriT
*/
public class Principal extends Application {
private static Stage stage; //uma janela
private static qtdRegistrosDAO aQtdRegistrosDAO;
public Principal() {
}
private void blockMultiInstance() {
try {
ServerSocket serverSocket = new ServerSocket(9581);
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, "Software já está aberto!", "Atenção", JOptionPane.WARNING_MESSAGE);
System.exit(0);
}
}
private void backup (){
try {
Scheduler sx = StdSchedulerFactory.getDefaultScheduler();
JobDetail jx = JobBuilder.newJob(BackupJob.class).build();
Trigger tx = TriggerBuilder.newTrigger().withIdentity("CroneTrigger2")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(60).repeatForever()).build();
try {
sx.start();
try {
sx.scheduleJob(jx,tx);
} catch (Exception e) {
e.printStackTrace();
}
} catch (SchedulerException se) {
se.printStackTrace();
}
} catch (SchedulerException ex) {
Logger.getLogger(Principal.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void stop() {
UsuarioDAO u = new UsuarioDAO();
u.setOffiline();
s.shutdown();
Platform.exit();
System.exit(0);
}
#Override
public void start(Stage stage) throws Exception {
Scheduler s = StdSchedulerFactory.getDefaultScheduler();
JobDetail j = JobBuilder.newJob(ChecarJob.class).build();
Trigger t = TriggerBuilder.newTrigger().withIdentity("CroneTrigger")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(60).repeatForever()).build();
try {
s.start();
try {
s.scheduleJob(j,t);
} catch (Exception e) {
e.printStackTrace();
}
} catch (SchedulerException se) {
se.printStackTrace();
}
Parent root = FXMLLoader.load(getClass().getResource("/fxml/Principal.fxml")); //carrega fxml
Scene scene = new Scene(root); //coloca o fxml em uma cena
stage.setScene(scene); // coloca a cena em uma janela
stage.show(); //abre a janela
setStage(stage);
}
public static Stage getStage() {
return stage;
}
public static void setStage(Stage stage) {
Principal.stage = stage;
}
public static void main(String[] args) {
launch(args);
}
}
If I raise her out of my start stage I can't
Scheduler s = StdSchedulerFactory.getDefaultScheduler();
JobDetail j = JobBuilder.newJob(ChecarJob.class).build();
Trigger t = TriggerBuilder.newTrigger().withIdentity("CroneTrigger"
Define a reference to the scheduler as a member of the Application
class.
Assign the scheduler reference in your start method.
When the application is stopped, call the appropriate method on the scheduler to safely shut it down.
Sample code
public class Principal extends Application {
private Scheduler s;
public void start(Stage stage) throws Exception {
s = StdSchedulerFactory.getDefaultScheduler();
// other work ...
}
public void stop() {
if (s != null) {
// pass true as a parameter if you want to wait
// for scheduled jobs to complete
// (though it will hang UI if you do that on FX thread).
s.shutdown();
}
}
}
There may be other issues with your code (I haven't checked), and I don't know if this answer will solve the core of your problem, but it will allow you to define a Scheduler instance as a reference in your application, which seems to be something you are asking for.

Update label from nested function called from Task API in JavaFX

I am performing some background task using this class
class Download extends Task{
protected Object call() throws Exception {
try {
updateMessage("Establishing Connection");
DownloadHelper downloadHelper = new DownloadHelper();
downloadHelper.performTask();
return null;
} catch (IOException | ParseException ex) {
logger.error(ExceptionUtils.getStackTrace(ex));
throw ex;
}
}
}
This Task in turn calls DownloadHelper to perform some task.
class DownloadHelper{
public DownloadHelper(){
}
public void performTask(){
----
----
}
}
Is there a way to update the status message of the Task API (updateMessage()) from the DownloadHelper class.?
The expedient approach is to pass a reference to the Download task as a parameter to the DownloadHelper constructor. To minimize coupling, you can instead pass a reference to your implementation of updateMessage() as a parameter of type Consumer, "an operation that accepts a single input argument and returns no result."
DownloadHelper helper = new DownloadHelper(this::updateMessage);
Your helper's implementation of performTask() can then ask the updater to accept() messages as needed.
Consumer<String> updater;
public DownloadHelper(Consumer<String> updater) {
this.updater = updater;
}
public void performTask() {
updater.accept("Helper message");
}
A related example is seen here.
import java.util.function.Consumer;
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
/**
* #see https://stackoverflow.com/q/45708923/230513
*/
public class MessageTest extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("MessageTest");
StackPane root = new StackPane();
Label label = new Label();
root.getChildren().add(label);
Scene scene = new Scene(root, 320, 120);
primaryStage.setScene(scene);
primaryStage.show();
Download task = new Download();
task.messageProperty().addListener((Observable o) -> {
label.setText(task.getMessage());
});
Thread thread = new Thread(task);
thread.setDaemon(true);
thread.start();
}
private static class Download extends Task<String> {
#Override
protected String call() throws Exception {
updateMessage("Establishing connection");
DownloadHelper helper = new DownloadHelper(this::updateMessage);
helper.performTask();
return "MessageTest";
}
#Override
protected void updateMessage(String message) {
super.updateMessage(message);
}
}
private static class DownloadHelper {
Consumer<String> updater;
public DownloadHelper(Consumer<String> updater) {
this.updater = updater;
}
public void performTask() {
updater.accept("Helper message");
}
}
public static void main(String[] args) {
launch(args);
}
}

Simple example for 'ScheduledService' in Javafx

I am a student and learning JavaFX since a month.
I am developing a application where I want a service to repeatedly start again after its execution of the task. For this I have come to know that 'ScheduledService' is used.
So can anybody please explain the use of scheduledservice with simple example and also how it differs from the 'Service' in JavaFX. Thanks ;)
EDIT : How can I define that this ScheduledService named DataThread should be restarted every 5 seconds ?
public class DataThread extends ScheduledService<Void>
{
#Override
public Task<Void> createTask() {
return new Task<Void>() {
#Override
public Void call() throws Exception {
for(i=0;i<10;i++)
{
System.out.println(""+i);
}
return null;
}
};
}
}
Considering you have a sound knowledge of Service class. ScheduledService is just a Service with a Scheduling functionality.
From the docs
The ScheduledService is a Service which will automatically restart itself after a successful execution, and under some conditions will restart even in case of failure
So we can say it as,
Service -> Execute One Task
ScheduledService -> Execute Same Task at regular intervals
A very simple example of Scheduled Service is the TimerService, which counts the number of times the Service Task has been called. It is scheduled to call it every 1 second
import java.util.concurrent.atomic.AtomicInteger;
import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.concurrent.WorkerStateEvent;
import javafx.event.EventHandler;
import javafx.stage.Stage;
import javafx.util.Duration;
public class TimerServiceApp extends Application {
#Override
public void start(Stage stage) throws Exception {
TimerService service = new TimerService();
AtomicInteger count = new AtomicInteger(0);
service.setCount(count.get());
service.setPeriod(Duration.seconds(1));
service.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent t) {
System.out.println("Called : " + t.getSource().getValue()
+ " time(s)");
count.set((int) t.getSource().getValue());
}
});
service.start();
}
public static void main(String[] args) {
launch();
}
private static class TimerService extends ScheduledService<Integer> {
private IntegerProperty count = new SimpleIntegerProperty();
public final void setCount(Integer value) {
count.set(value);
}
public final Integer getCount() {
return count.get();
}
public final IntegerProperty countProperty() {
return count;
}
protected Task<Integer> createTask() {
return new Task<Integer>() {
protected Integer call() {
//Adds 1 to the count
count.set(getCount() + 1);
return getCount();
}
};
}
}
}

How to quickly stop seda in camel

I have a camel route with a splitter (using streaming) that sends messages to a seda queue to be processed. When I'm trying to stop the application gently, the seda queue doesn't stop immediately, it is processing all the messages before finally shutting down.
What can I do to stop it right away?
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.camel.Exchange;
import org.apache.camel.Processor;
import org.apache.camel.builder.ExpressionBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.main.Main;
public class MySedaShutdownTest extends RouteBuilder {
#Override
public void configure() throws Exception {
onException(Exception.class)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println("exception");
}
});
from("timer:myTimer?repeatCount=1")
.split(ExpressionBuilder.beanExpression(new MySplitter(), "myIterator"))
.streaming()
.to("seda:mySeda");
from("seda:mySeda")
.throttle(1)
.process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println("processing: " + exchange.getIn().getBody()
+ "; app status: " + exchange.getContext().getStatus());
}
});
}
public static class MySplitter {
public Iterator<String> myIterator() {
List<String> values = new ArrayList<String>();
for (int i = 0; i < 10; i++) {
values.add("string nr : " + i);
}
System.out.println("in myIterator");
return values.iterator();
}
}
public static void main(String[] a) throws Exception {
final Main main = new Main();
new Thread(new Runnable() {
#Override
public void run() {
try {
TimeUnit.SECONDS.sleep(4);
} catch (InterruptedException e) {
e.printStackTrace();
}
try {
System.out.println("invoking shutdown");
main.shutdown();
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
System.out.println("starting app");
main.enableHangupSupport();
main.addRouteBuilder(new MySedaShutdownTest());
main.run();
}
}
There is a purgeQueue method on the SedaEndpoint. So you can get the endpoint and call this method. You can also access it from JMX.
A bit related we have this ticket for improvement
https://issues.apache.org/jira/browse/CAMEL-5911
And I logged a ticket for this
https://issues.apache.org/jira/browse/CAMEL-6405
Just add string below as first line of your configure() method:
getContext().getShutdownStrategy().setTimeout(1);
It will reduce shutdown timeout from 300 seconds (default) to 1
See more info on controlling start-up and shutdown of routes.

Using EJBs in thread spawned from Servlet - bad practice?

We have a servlet as follows:
public class CacheRefresher extends HttpServlet {
private static final long START_TIMEOUT = 120*1000;
public void init(ServletConfig servletConfig) throws ServletException {
super.init(servletConfig);
new Thread(new Worker()).start();
}
private class Worker implements Runnable {
public Worker() { }
public void run() {
try {
Thread.sleep(START_TIMEOUT);
} catch (InterruptedException e) {
}
while(true) {
MyService myService = null;
try {
myService = ServiceFactory.getInstance().getMyService();
myService.doSomething();
} catch (Exception ex){
ex.printStackTrace();
}finally {
ServiceFactory.getInstance().releaseMyService(myService);
}
try {
Thread.sleep(timeout);
} catch (InterruptedException e) {
}
}
}
}
}
Its purpose is to periodically call a service. There will only be a single instance of this Servlet, which will be created on server startup. MyService is an EJB.
How bad is this? I know spawning threads from EJBs is not allowed, but what about the other way around? What will happen on server shutdown?
Conceptualy i dont see a problem with invoking ejb methods from multiple threads (even if you created the threads yourself). For the ejb-container that will be just another client among others.
From your example it looks like the soul purpose of you servlet is to start a bunch of timers. If you can use ejb 3.1, there is java ee standard way to do that.
First a Singleton ejb that launches the timers on startup
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.ejb.Singleton;
import javax.ejb.Startup;
#Singleton
#Startup
public class SingletonBean {
#EJB
LabBean labBean;
#PostConstruct
public void init() {
long interval = 4000;
long initialExpiration = 2000;
labBean.startTimer(initialExpiration, interval, "MyTimer");
}
}
Then a SLSB that handles the timeout:
import javax.annotation.Resource;
import javax.ejb.Stateless;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerConfig;
import javax.ejb.TimerService;
#Stateless
public class LabBean {
#Resource
protected TimerService timerService;
#Timeout
public void timeoutHandler(Timer timer) {
String name = timer.getInfo().toString();
System.out.println("Timer name=" + name);
}
public void stopTimer(String name) {
for (Object o : this.timerService.getTimers())
if (((Timer) o).getInfo().toString().startsWith(name)){
((Timer)o).cancel();
}
}
public void startTimer(long initialExpiration, long interval, String name){
stopTimer(name);
TimerConfig config = new TimerConfig();
config.setInfo(name);
config.setPersistent(false);
timerService.createIntervalTimer(initialExpiration, interval, config);
}
}

Resources