In my application, I load a table with data.
In this tab, one column come from a webservice which can make some times to answer.
So I treat this one in pool thread to avoid to block the screen like this:
final ObservableList<StockListBean> list = FXCollections
.observableArrayList();
list.addAll(stocksListMService.getStocksListRunning());
stocksList.setItems(list);
final ExecutorService executor = Executors.newFixedThreadPool(4);
for (final StockListBean stockListBean : list) {
executor.execute(new Task<Float>() {
#Override
protected Float call() throws Exception {
logger.debug("In jfx task for {}", stockListBean.getCode());
((StockListRunningBean)stockListBean).setActualPrice(stocksListMService.getActualPrice(stockListBean.getCode()));
columnActualPrice.setVisible(false);
columnActualPrice.setVisible(true);
return 0f;
}
});
}
Threads are well execute and data are well set in beans but I don't reach to refresh the tableView.
I try code in the snapshot. I try many other ways found on the web but nothing to do, the column is desperately empty.
If I keep the thread loop but without execute the service and set a default value, the column is not empty.
It's such a real problem with screen refresh.
How can I refresh this ?
Thanks.
Assuming your StockListRunningBean uses JavaFX observable properties, so that the TableView sees the changes, you shouldn't need to do anything additional to update the table. One problem with your code is that you're making changes to the UI (via changes to the StockListRunningBean price property) from a thread that's not the FX Application Thread.
Try this refactoring:
for (final StockListBean stockListBean : list) {
final int code = stockListBean.getCode(); // assuming int, change as required
final Task<Float> task = new Task<Float>() {
#Override
protected Float call() throws Exception {
logger.debug("In jfx task for {}", code);
return stocksListMService.getActualPrice(code);
}
};
task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent event) {
((StockListRunningBean)stockListBean).setActualPrice(task.getValue());
}
});
executor.execute(task);
}
Again, this assumes that your StockListRunnerBean has a
public FloatProperty actualPriceProperty() {...}
method and that the table column is properly bound to it.
Related
I'm trying to call some code within the vaadin framework which is longer running that will update the screen using push, however if the process is taking too long I want to be able to cancel it.
With that in mind I'm trying to use Guava's SimpleTimeLimiter class but no matter what I do I can't seem to stop the Vaadin process from stopping. I've tried both to put the SimpleTimeLimiter inside UI.getCurrent().access() method and outside of it but they both just continue to execute the process even if SimpleTimeLimiter throws a TimeoutException. However if I use the same code with a normal thread it seems to work...
public static void limitExecutionTime(Consumer<UI> lambda)
{
UI currentUI = UI.getCurrent();
UI.getCurrent().access(() ->
{
try
{
SimpleTimeLimiter.create(Executors.newSingleThreadExecutor()).callWithTimeout(new Callable<Void>()
{
#Override
public Void call()
{
// This is needed to deal how Vaadin 8 handles UI's
UI.setCurrent(currentUI);
lambda.accept();
return null;
}
}, 1, TimeUnit.SECONDS);
} catch (TimeoutException | InterruptedException | ExecutionException e) {
NotificationUtils.showError("Execution took beyond the maximum allowed time.");
currentUI.push();
}
});
}
In the above code if the method takes more than 1 second it will throw a TimeoutException and put up the notification window. However it will continue to execute the lambda.
As a result I've tried to do the opposite and put UI.getCurrent().access() in the public Void call() method but this had the exact same result...
You should call UI.access after your background task is ready to update it with some data. You use access method to do changes on the page that the user is viewing.
Background task execution
In your example, you are missing a way to pass task cancellation message to call method. In order to prepare for task cancellation from external event (for example cancel button click) then you need to take this into account in inside the task. The following example shows how you can offer cancel method using Future.cancel.
private void onCancelClick(Button.ClickEvent clickEvent) {
// This method is called from Vaadin UI thread. We will signal
// background task thread to stop.
futureResult.cancel(true);
}
Inside the actual task this can be handled in the following ways
private void simulateLongAndSlowCalculation() {
while (moreWorkTodo) {
if (Thread.currentThread().isInterrupted()) {
return;
}
try {
doSomeBlockingCallThatCanBeInterrupted();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
}
Starting task and UI.access
When starting task then view should create task and submit it to executor service.
private void onButtonClick(Button.ClickEvent clickEvent) {
// This runTask creates important link to current UI and the background task.
// "this" object in these onTask methods is the UI object that we want
// to update. We need to have someway to pass UI object to background
// thread. UI.getCurrent() could be a parameter that is passed to the
// task as well.
Future<String> futureResult = taskService.runTask(
this::onTaskDone,
this::onTaskCancel,
this::onTaskProgress);
progressDialog = new ProgressDialog(futureResult);
progressDialog.show();
}
Now UI.access method is only needed when we want to update UI. In this example, that can happen in the following cases
Task completed successfully
Task progress was updated
Task got cancelled
Note that all of the following methods this refers to the UI object that started the task. So we are updating the correct UI with result and not some other user's UI.
You should not need to call UI.setCurrent in your code.
private void onTaskProgress(double progress) {
logger.info("onTaskProgress: {}", progress);
access(() -> progressDialog.setProgress(progress));
}
private void onTaskCancel() {
logger.info("onTaskCancel");
access(() -> {
progressDialog.close();
setResult("Cancelled");
});
}
private void onTaskDone(String result) {
logger.info("onTaskDone");
access(() -> {
progressDialog.close();
setResult(result);
});
}
Example project
I pushed another project to github that shows how to cancel a background task from cancel button:
https://github.com/m1kah/vaadin-background-task
Edit: Added sections about background tasks and UI.access. Updated example project link to another example.
So, the scenario is like:
//Some code...
public Map<String, String> someFunction() {
for (final UserDetail user : userDetailList) {
// the following (below) code runs in background thread
// long running task
new RecordPersonalisedDao().getPendingRecordsForUid(user.getuId(), new RecordPersonalisedDao.OnResultFetched() {
#Override
public void onResult(Result result) {
// callback after the user is processed
// put some parameter from result to map
map.put(user.getName(), result.getTotal());
}
});
}
// return map only after all users are processed
return map;
}
As mentioned in the comment of above piece of code, I want the final map to be returned only after the entire list of user is processed.
I cannot change the functionality of RecordPersonalisedDao#getPendingRecordsForUid so as to make it run in the main thread only.
How do I achieve this in java ?
Edit: This type of problem can be faced in general. So, I want to understand the solution for the same in java.
To put my question simply, I want behaviour like
Run this code in background for all members in the array, and once it's done, send a callback.
(Roughly like)
[list_of_some_data]
.forEach( run this function )
.after(after the function is run in background for all members of list - return some value)
Before the loop, create a CountdownLatch with a count equal to the user list length. Inside the result handler, count down after updating the map. After the loopawait() the latch to be counted down, then return.
public Map<String, String> someFunction() {
CountDownLatch cdl = new CountDownLatch(userDetailsList.size());
for (final UserDetail user : userDetailList) {
// the following (below) code runs in background thread
// long running task
new RecordPersonalisedDao().getPendingRecordsForUid(user.getuId(), new RecordPersonalisedDao.OnResultFetched() {
#Override
public void onResult(Result result) {
// callback after the user is processed
// put some parameter from result to map
map.put(user.getName(), result.getTotal());
//We're done grabbing the results.. count down.
cdl.countDown();
}
});
}
//Block this thread until all the results are in.
cdl.await();
return map;
}
I'd like to effectively "poll" on a JavaFX TableView so that if a job is created in the database by another user the current user would pick it up (let's say every 5 seconds).
I have tried using Timer;
new Timer().schedule(new TimerTask() {
#Override
public void run() {
try {
newI(connection, finalQuery, adminID);
} catch (SQLException e) {
e.printStackTrace();
}
}
}, 0, 5000);
However this gets the following error: Exception in thread "Timer-0" java.lang.IllegalStateException: This operation is permitted on the event thread only; currentThread = Timer-0 which I assume means that it is not supported in JavaFX? How am I able to periodically update the TableView in JavaFX?
You can use a ScehduleService- something like this...
private class MyTimerService extends ScheduledService<Collection<MyDTO>> {
#Override
protected Task<Collection<MyDTO>> createTask() {
return new Task<Collection<MyDTO>>() {
#Override
protected Collection<MyDTO> call() throws ClientProtocolException, IOException {
//Do your work here to build the collection (or what ever DTO).
return yourCollection;
}
};
}
}
//Instead of time in your code above, set up your schedule and repeat period.
service = new MyTimerService () ;
//How long the repeat is
service.setPeriod(Duration.seconds(5));
//How long the initial wait is
service.setDelay(Duration.seconds(5));
service.setOnSucceeded(event -> Platform.runLater(() -> {
//where items are the details in your table
items = service.getValue();
}));
//start the service
service.start();
I can load data from query result in JTable nicely.
SELECT * FROM 'customer info';
Make a result set and place to JTable is easy to handle.
But problem is, as my database table is so big and it takes so much time to load. My application totally freeze until current task complete. I know swing worker can perform a background task. I study this, but i find no any appropriate solution in my case. Because i already write a lot of function in basic way.
So finally what i need?
I need a Class, design with swing worker by which i can easily use it's object anywhere in my application. suppose i send my query string and JTable in this class constructor. Then it automatically starts a background thread to make result set and place it in JTable.
ok now its work in my case :) here is my own solution ....
public class UsableSwingWorkerThread extends SwingWorker<ResultSet, Object> {
JTable table;
String query;
public UsableSwingWorkerThread(JTable table, String query) {
this.table = table;
this.query = query;
}
#Override
protected ResultSet doInBackground() throws Exception {
return DatabaseFunctionClass.con.prepareStatement(query).executeQuery();
}
void loadTable(TableModel tb) {
new UsableDefaultLoadTable(tb, table);
}
#Override
protected void done() {
try {
loadTable(DbUtils.resultSetToTableModel(get()));
} catch (Exception ignore) {
StaticAccess.showMassageDialog(StaticAccess.mainFrame, "Fail to load. try again later..", ignore);
}
}
}
I would like to have an application which either loads or saves data through a HTTP request, however the data must interact with the UI thread. Ideally, I would like a single thread to use an IF statement on a message to determine if the request is to "load" or "save".
What would be the simplest way of doing this with the smallest amount of code?
Also, do instances of Handlers run on individual threads?
EDIT: This is the code I am using now:
Handler doStuff = new Handler(){
#Override
public void handleMessage(Message msg){
if(msg.what == 1){
// Load all the information.
// Get the ID from sharedPrefs
SharedPreferences details= getSharedPreferences("details", 0);
String ID = patDetails.getString("id", "error");
// Load up the ID from HTTP
String patInfo = httpInc.getURLContent("info.php?no="+AES.encrypt("387gk3hjbo8sgslksjho87s", ID));
// Separate all the details
patientInfo = patInfo.split("~");
}
if(msg.what == 2){
// Save the data
}
}
};
Eclipse halts the debugging and displays, "Source not found" for StrictMode.class
I suppose it's because it's using the Main thread to access the internet although it's running in individual threads.
Any idea.
Handlers do run on individual threads. Check that link. You should also check out AsyncTask.
I would propose submitting the jobs as Runnable to a single-threaded ExecutorService:
public class SomeClass {
private ExecutorService execService = Executors.newSingleThreadExecutor();
public void doSomething() {
final String someUiData = // retrieve data from UI
execService.submit(new Runnable() {
#Override
public void run() {
// so something time-consuming, which will be executed asynchronously from the UI thread
// you can also access someUiData here...
}
});
}
}
This way, the UI thread will not block whereas you can easily submit a different Runnable for different operations and the ExecutorService will completely take care of keeping it async.
Edit: If you need to interact with the UI, do so before becoming asynchronous and keep the result in final variables.