I want to append some log strings to a textArea in my vaadin app.
VaadinSqlApplication.logger.info("Export start");
logTextArea.setValue("Export Start");
...
logTextArea.setValue("Export done");
but the textArea only changed after the whole function ended.
Is there any function like in JavaFX :
Platform.runLater(new Runnable() {
#Override
public void run() {
//change text
});
or in swing :
EventQueue.invokeLater(changer);
You need to enable "push" on the Vaadin UI, as by default all updates to the UI will be grouped and sent as a single response (like you say: when the function returns).
Have a look at this page, it explains the whole setup very nicely:
https://vaadin.com/docs/v8/framework/advanced/advanced-push.html
BTW, once push is enabled, you will need a similar technique as the SwingUtilities.invokeLater that you mentioned:
Making changes to a UI object from another thread and pushing them to
the browser requires locking the user session when accessing the UI.
Otherwise, the UI update done from another thread could conflict with
a regular event-driven update and cause either data corruption or
deadlocks. Because of this, you may only access an UI using the
access() method, which locks the session to prevent conflicts. It
takes a Runnable which it executes as its parameter.
For example:
ui.access(new Runnable() {
#Override
public void run() {
...
}
});
Related
I have page with grid. I am loading data through service to grid. Loading and computing all data takes about 20-40 seconds. When I press button to get data, page start loading (classical in vaadin top loading indicator start loading).
My question is, how can I stop loading/waiting for data?
I can't stop searching process on that server I am getting data from, it dont have this functionality, I can only request for data, and wait for them.
Should I stop some thread? should i use something like this.getUI... and somewhere here stop it?
I am using vaadin 7.7.4
Thank you :)
You should use threads for this.
You will need to separate your logic, that the main thread does add all components to the UI.
This thread then also needs to spawn a new thread which does fetch the data and then updates the UI accordingly.
To update the UI once the data has been fetched from the backend you will need to activate push in your UI.
Don't forget to synchronise thread access to the UI with something like:
ui.access(new Runnable() {
#Override
public void run() {
...grid_update_with_new_data... ;
}
});
The fetching of the data should occur outside the ui.access method, otherwise your UI will freeze during backend data loading.
See this post for more technical details
Using Thread with Vaadin? and https://vaadin.com/docs/v7/framework/advanced/advanced-push.html
#André Schild This is simplified code. When I hit search button, app start searching, no problem with that. Problem is how to STOP searching, before its done. Enough for me is to stop waiting for response, and stop loading bar at top of the page, but I dont know how to achive this.
#SpringComponent
#UIScope
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class DistraintSearchComponent extends CustomComponent {
#Autowired
private Service service
private Button searchButton = new Button("Search");
public void init(){
searchButton.addClickListener(new ClickListener() {
#Override
public void buttonClick(ClickEvent event) {
List<Results> results = service.findByFilter(filter);
refreshGrid(results);
}
});
}
}
I'm creating a game modification that uses MySQL to get and store player data. It refreshes it's client data from the database every 4 seconds, but because it is blocking, it freezes about a second as it gets the data.
Is there any simple way to execute the command async?
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.submit(() -> {
//Your jdbc call here
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// Here, we can safely update the GUI
// because we'll be called from the
// event dispatch thread
MW.append(foo);
}
});
});
If you are not doing so already, use Threads. Override the run() method for things you want to run asynchronously in Java.
Also just making sure, you wait until the server returns the database data before performing another request right?
This topic is around in countless articles of "not blocking the JavaFX UI during a long lasting call into something time consuming (server)". I know that and I googled and tried a lot.
Most of the "Internet" explains that long lasting calls caused by JavaFX events need to be made in an extra thread. After the thread is finished, you update the FX UI with the results via Platform.runLater(). Most of the FX Libraries encapsulate this with sophisticated code constructs (really cool stuff).
My current problem with it is: We are migrating a Swing rich client to JavaFX. It is a huge one, so we have to constantly include/replace JavaFX UI into it, until it is a full FX client.
There is some functionality in the client that does a server call and has to wait before the user can continue with his work.
The server uses JEE6 with stateless session beans and interfaces. The interfaces are known to the client and with a little library of our own, we implemented a little proxy hiding away the communication layer from the client.
The client just creates a "RemoteProxy" with the library then just calling the remote interface and the library propagates the call to the server. The method is called and the result or Exception transported back to the client. For the client this appears like a local call.
Here is the problem. A typical code fragment looks like this:
...
ServerRemoteInterface myServerRemoteProxy = Helper.getProxyForInterface(ServerRemoteInterface.class) ;
...
ComplexResult result = myServerRemoteProxy.doThingsOnServer(SerializableParameter p1 ...)
doSomethingUsefull() ;
The call to the server is triggered in the Swing UI thread (by a listener). It stops the execution of the program (Listener Code) although it is done in an extra thread. "doSomethingUsefull()" is called after the server got back. The developer does not have to take care about threading here.
How is it accomplished? By using the "Spin Library" (http://spin.sourceforge.net/).
It does some clever tricks with the Swing EDT.
An alternative would be to use a modal Dialog, but we decided not not have an extra window, but have a glasspane disabling some UI components instead.
So long explanation and short question...
Is there something similar for JavaFX helping us to seamlessly call a server, stop the program execution until it got back and NOT blocking the JavaFX UI? Best would be if it can work together with Java Swing parts of code.
EDIT... Adding an very compressed example for demonstration of the use with hidden JDialog.
We need the server remote interface. Any interface will do.
public interface ServerRemoteInterface
{
public String method1() ; // String result in our case for test purposes
public String method2() ; // Exceptions would also be possible.
}
Then we need the Proxy Invocation Handler
public class ServerProxy implements InvocationHandler
{
public Object result;
JDialog modalDialog = new JDialog((Frame)null, true);
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
ServerCallHelper serverThread = new ServerCallHelper(args, method) ;
modalDialog.setLocation(4000, 5000); // as if using Spin library. Here we use the dialog to block in a location off the screen.
serverThread.start();
modalDialog.setVisible(true) ;
return result;
}
class ServerCallHelper extends Thread
{
Object[] args;
Method method;
public ServerCallHelper(Object[] args, Method method)
{
this.args = args;
this.method = method;
}
public void run()
{
// do a server call via rmi or tunnel it via http, REST or whatever and provide the call parameters. On the server side there must be a receiver propagating the call to the wanted session bean.
// here we just do a simulation
try
{
Thread.sleep(3000);
} catch (InterruptedException e)
{
// interupt is ok here.
}
// now hand the result from the call back. we simulate a fix result
// We also could have caught the Exception and deal with it.
result = "Simulated Result" ;
// Since we are in the worker thread => inform EDT To close the dialog.
SwingUtilities.invokeLater(()->modalDialog.setVisible(false));
}
}
}
And finally some code to show the functionality
public class SampleUI extends JFrame
{
JButton serverCallButton = new JButton("Call server") ;
JLabel resultLabel = new JLabel("No result so far") ;
public SampleUI()
{
JPanel cont = new JPanel() ;
cont.add(serverCallButton) ;
cont.add(resultLabel) ;
this.setContentPane(cont);
serverCallButton.addActionListener((e)->processServerButtonCall());
}
private void processServerButtonCall()
{
ServerRemoteInterface serverAccess = (ServerRemoteInterface) Proxy.newProxyInstance(SampleUI.class.getClassLoader(), new Class[]{ServerRemoteInterface.class}, new ServerProxy());
String result = serverAccess.method1() ;
resultLabel.setText(result);
}
public static void main(String[] args)
{
SampleUI sampleUI = new SampleUI() ;
sampleUI.pack();
sampleUI.setVisible(true);
}
}
The example is very compressed but should show the principle. As a developer I do not have to take care that the call to the server is really a server call. To me it's like a local call. I do not even have to take care that I am in the EDT Thread, because i just am.
As I said it would work the same way in FX with a modal stage. I tried to set it to opaque 0.0 => It is not drawn anyway. This works.
The question remains: Are there ways to get around the extra JDialog or Stage ?
If I understood your intentions correctly, this is a use case for Future:
CompletableFuture.supplyAsync(() -> myServerRemoteProxy.doThingsOnServer(...))
.thenAccept(result -> doSomethingUseful(result));
Both the server call and doSomethingUseful will be executed in another thread, so you need to use Platform.runLater in doSomethingUseful if you want to access the scene graph.
Like Tomas said the solution is a nested Event Loop.
Currectly Java FX already has such an implementation:
com.sun.javafx.tk.Toolkit.getToolkit()
provides the methods enterNestedEventLoop and exitNestedEventLoop.
From the package name you can tell that it is sun(oracle) specific and should not be used. I read that people already asked Oracle to move it it "Platform" because it is a very useful feature.
Maybe we use it anyway :-)
So as far as I understood, all the swing components should be created, modified and queried only from the EDT.
So if I happen to press a JButton "submit" let's say, that will pack up all the information from the text boxes, send that data to controller and then controller will send it to other controllers which will eventually send stuff to the server. What thread is the action for that button is running on? If it is running on EDT, how do I exit it to send the data to controller from the main thread? Should I even use main thread to send data to server from the controller?
So what I am saying is this
java.awt.EventQueue.invokeLater(new Runnable()
{
#Override
public void run()
{
JButton button = new JButton("Submit");
button.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
// WHAT THREAD DO ACTIONS HERE RUN ON?
// AND HOW DO I MAKE THEM RUN ON MAIN THREAD?
// AND WHAT THREAD SHOULD I RUN THING ON HERE?
}
});
}
});
Any action triggered from Swing will run on the EDT. So the code in your actionPerformed method will already be executed on the EDT, without any special handling by you.
To start a long-running task, like sending data to a server, use a SwingWorker or a Callable and an ExecutorService.
I prefer using a SwingWorker when implementing a Swing UI, as has it a useful API for publishing updates and makes the callbacks when the task is done automatically happen on the EDT.
I have a simple client-server program. In one thread GUI is running, when I click "connect" button on GUI, I connect to server in new thread, after some time I receive some data from server and I want to pass it to GUI - how can I do that?
Pass a reference to your GUI object to the reading thread, and have the reading thread invoke a method of the GUI instance when he has received the data.
If the GUI is a Swing GUI, you'll need to wrap the call into SwingUtilities.invokeLater():
Thread readingThread = new MyReadingThread(gui);
readingThread.start();
and in the reading thread:
String data = readData();
SwingUtilities.invokeLater(new Runnable() {
public void run() {
gui.dataHasBeenReceived(data);
}
});
Note that SwingWorker is designed specifically for this kind of use-case.
Generally - by passing a callback object to the new thread, and whenever you have results, invoke that callback, which in turn updates the GUI.
The "callback" can be an object implementing a custom interface of yours, that takes the result as argument.
BackgroundThread background = new BackgroundThread();
backgroundThread.setCallback(new Foo() {
public void updateGUI(Bar bar) {
//...
}
}
Than, when you have the information available in the background thread, you can just call:
getCallback().updateGUI(bar);