Update label from nested function called from Task API in JavaFX - 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);
}
}

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.

Exception in Application constructor while making a constructor

When I am running the below mentioned code it is working
import javafx.application.Application;
public class Client {
public static void main(String[] args){
Test t2 = new Test();
Application.launch(t2.getClass(),args);
}
}
where the test class is
package com.temp.com.serverclient;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Test extends Application {
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("No Main");
StackPane root = new StackPane();
root.getChildren().add(new Label("It worked!"));
primaryStage.setScene(new Scene(root, 300, 120));
primaryStage.show();
}
}
But if I am trying to add constructor,it is getting Exception in Application constructor,Error.
The code is
package com.temp.com.serverclient;
import javafx.application.Application;
public class Client {
public static void main(String[] args){
Test t1 = new Test("Pass this String to Constructor");
Application.launch(t1.getClass(),args);
}
}
Test class
package com.temp.com.serverclient;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class Test extends Application {
String str;
public Test(String str) {
this.str = str;
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("No Main");
StackPane root = new StackPane();
root.getChildren().add(new Label("It worked!"));
primaryStage.setScene(new Scene(root, 300, 120));
primaryStage.show();
}
}
How Can I sovle the problem? I need to pass the String to collect information from previous class.
Application.launch always uses a public parameterless constructor to create a instance of the application class to launch. (It does not provide any benefit to create a instance in the main method BTW. Simply pass the class without creating a instance, i.e. Application.launch(Test.class, args);.)
In fact you can only pass String parameters to the new instance of your application class without using static members and it's done via the args parameter of Application.launch:
public class Client {
public static void main(String[] args) {
Application.launch(Test.class, "Pass this String to Constructor");
}
}
public class Test extends Application {
String str;
#Override
public init() {
this.str = getParameters().getRaw().get(0);
}
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle("No Main");
StackPane root = new StackPane();
root.getChildren().add(new Label("It worked!"));
primaryStage.setScene(new Scene(root, 300, 120));
primaryStage.show();
}
}
Note that accessing the parameters property is also possible for the start method.
JavaFX 9 introduced a new possiblility: using Platform.startup but you need to handle the lifecycle of the application class yourself:
Application app = new Test("Pass this String to Constructor");
app.init();
Platform.startup(() -> {
Stage stage = new Stage();
try {
app.start(stage);
} catch (Exception ex) {
throw new IllegalStateException(ex);
}
});
This does not properly call the Application.stop method though. Also the parameters are not assigned.
AFAIK, Application.launch creates a new instance of Test. Therefore, you need another way to get the value to the instance, e. g. using a static getter method in your Client class which is called from Test

How to wait cancellation of task after Service#cancel?

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);
}
}

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();
}
};
}
}
}

JavaFx 2.0 FXML project How to access variables and project structure

I am making an IRC client in a javafx fxml project.
I am having difficulties understanding the structure of the project. I get the MVC pattern however i don't know where i have to position the main code part of the client(object initialisation and communication start).
I want to be able to update a textarea with a String from a serverConnection object when the server sends a message.
So where do i position the code and how do i notify my controller that it has to update its text passing the String simultaneously?
This is the application class:
package jircclient;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import java.util.concurrent.*;
public class JircClient extends Application
{
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setTitle("jircClient");
stage.setScene(scene);
stage.show();
System.out.println("Stage SET!");
//This is where i thought that i could write the main part of the program
//but the stage will not load unless the start method finishes
//Creating a serverConnetion is not a problem since it's only an object
//but starting the communication does not let the method we are in to exit
//since it sticks in a while loop forever until i probably cancel it using a
//button (not implemented yet)
//serverConnection server1 = new serverConnection();
//server1.startCommunication();
}
public static void main(String[] args) throws Exception
{
//In this method i cannot go further unless i close the stage-window
//The purpose of it is to launch the start method
Application.launch(args);
}
}
This is the controller class:
package jircclient;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextArea;
public class FXMLDocumentController implements Initializable
{
#FXML
private TextArea txt;
#FXML
private void handleButtonAction(ActionEvent event)
{
//In here i will be handling events, however i will need to have
//access to serverConnection objects
System.out.println("You clicked me!");
}
#Override
public void initialize(URL url, ResourceBundle rb)
{
}
}
This is the serverConnection class:
package jircclient;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.ArrayList;
public class serverConnection
{
//VARIABLES
private clientInfo client;
private String server_to_connect = "someserver";
private String channel_to_connect = "#somechannel";
private String serv_resp;
private int port = 6667;
private Socket socket;
private BufferedWriter writer;
private BufferedReader reader;
private ArrayList<channel> channels;
//DEFAULT CONSTRUCTOR
public serverConnection() {client = new clientInfo("test1", "test1", "test1");}
//FULL CONSTRUCTOR
public serverConnection(String server_to_connect, int port, String channel_to_connect) throws IOException
{
client = new clientInfo("test1", "test1", "test1");
this.server_to_connect = server_to_connect;
this.port = port;
this.channel_to_connect = server_to_connect;
try
{
//Creating socket connection
this.socket = new Socket(this.server_to_connect,port);
//Socket output writer
writer = new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream()));
//Socket input writer
reader = new BufferedReader(
new InputStreamReader(socket.getInputStream()));
serv_resp = null;
System.out.println("Connection established.");
}
catch(UnknownHostException exc)
{
System.out.println("ERROR: "+ exc.toString());
}
catch(IOException exc)
{
System.out.println("ERROR: "+ exc.toString());
}
finally
{
System.out.println("Closing connection.");
socket.close();
}
}
//server response getter
public String getServerResponse()
{
return serv_resp;
}
//Introduction to server and listen
public void startCommunication() throws IOException
{
this.socket = new Socket(this.server_to_connect,port);
writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
serv_resp = null;
writer.write("NICK " + client.getClientNickname() + "\r\n");
writer.write("USER " + client.getClientLogin() + " 0 * : " +
client.getClientRealName() + "\r\n");
while ((serv_resp = reader.readLine()) != null)
{
System.out.println(serv_resp);
//FXMLDocumentController.txt.setText(serv_resp);
if (serv_resp.indexOf("004") >= 0)
{
break;
}
else if (serv_resp.indexOf("433") >= 0)
{
System.out.println("Nickname is already in use.");
return;
}
}
//Get channel list
writer.write("LIST \r\n");
writer.flush();
//Join desired client
writer.write("JOIN " + channel_to_connect + "\r\n");
writer.flush();
//keep listening
while ((serv_resp = reader.readLine()) != null)
{
//FXMLDocumentController.txt.setText(serv_resp);
if (serv_resp.startsWith("PING "))
{
this.pingPong();
} else
{
System.out.println(serv_resp);
}
}
}
//Ping respond
public void pingPong() throws IOException
{
writer.write("PONG " + serv_resp.substring(5) + "\r\n");
writer.flush();
}
}
I believe there is no need to add the fxml document since it's big and there is no need.
I also have to state that oracle tutorials were not helpful as they only use the event handling in the controller and they don't implement any other logic.
Here is one possible way to do this:
Give your ServerConnection class a callback to call when it receives a message:
public class ServerConnection {
private Consumer<String> messageCallback ;
public void setMessageCallback(Consumer<String> messageCallback) {
this.messageCallback = mesasgeCallback ;
}
// other fields and methods as before...
public void startCommunication() throws IOException {
// code as before ...
while ((servResp = reader.readLine()) !=null) {
if (messageCallback != null) {
messageCallback.accept(servResp);
}
// etc....
}
// etc
}
}
Now instantiate your ServerConnection class from the controller and pass it a callback:
public class FXMLDocumentController {
ServerConnection serverConnection ;
// ...
public void initialize() {
serverConnection = new ServerConnection();
serverConnection.setMessageCallback(message ->
Platform.runLater(() -> txt.appendText(message+"\n")));
Thread serverThread = new Thread(() -> serverConnection.startListening());
serverThread.setDaemon(true); // thread will not stop application from exiting
serverThread.start();
}
// ...
}

Resources