NullPointerException while fast updating TextField - java

I'm working on an app, that searches some files in the directed folder and prints them in TableView<myFile> foundFilesList, that is stored in the Main class. myFile just extends File a bit. The searching is done using service in background thread, that puts found data to ObservableList<myFile> filesOfUser.
I want to display current amount of find files in TextField foundFilesAmount in the same view, where TableView with files is located -- ResultsView
To do that, I added a ListChangeListener for foundFilesList to ResultsView controller, that uses method setText to print current size of filesOfUser. It looks like:
Main.filesOfUser.addListener(new ListChangeListener<myFile>() {
#Override
public void onChanged(Change<? extends myFile> c) {
while (c.next()){
if (c.wasAdded())
setCounter(c.getAddedSize());
}
}
});
void setCounter (int number) contains only
int currValue = Integer.valueOf(foundFilesAmount.getText());
foundFilesAmount.setText(String.valueOf(currValue + number));
And now what the problem is. Textfield with current amount of find files is updated very fast, and from one moment it stops doing it. In the console I see lots of repeated NullPointerException's from JavaFX Application Thread. Its' contents:
Exception in thread "JavaFX Application Thread" java.lang.NullPointerException
at com.sun.javafx.text.PrismTextLayout.getRuns(PrismTextLayout.java:236)
at javafx.scene.text.Text.getRuns(Text.java:317)
at javafx.scene.text.Text.updatePGText(Text.java:1465)
at javafx.scene.text.Text.impl_updatePeer(Text.java:1500)
at javafx.scene.Node.impl_syncPeer(Node.java:503)
at javafx.scene.Scene$ScenePulseListener.synchronizeSceneNodes(Scene.java:2290)
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2419)
at com.sun.javafx.tk.Toolkit.lambda$runPulse$30(Toolkit.java:355)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.tk.Toolkit.runPulse(Toolkit.java:354)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:381)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:510)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:490)
at com.sun.javafx.tk.quantum.QuantumToolkit.lambda$runToolkit$404(QuantumToolkit.java:319)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
I tried to set sort of delay before using setText, like updating value in foundFilesAmount only after 5'th, 10'th, 15'th update, etc. But if the search works longer, exceptions are still thrown.
Is there a correct method to show current amount of found files, that contains real amount and doesn't cause so much exceptions?
Thanks in advance.

The correct way is not doing the updates of the UI on a thread other than the application thread. This can otherwise lead to issues with rendering/layouting.
You could use a Task to do the updates to the updates however:
Task<ObservableList<myFile>> task = new Task<
Task<ObservableList<myFile>>() {
#Override
protected ObservableList<myFile> call() throws Exception {
ObservableList<myFile> files = FXCollections.observableArrayList();
while (...) {
...
files.add(...);
updateMessage(Integer.toString(files.size()));
...
}
return files;
}
};
task.setOnSucceeded(evt -> tableView.setItems(task.getValue()));
Thread t = new Thread(task);
textField.textProperty().bind(task.messageProperty());
t.setDaemon(true);
t.start();

try something like this:
Platform.runLater(new Runnable() {
#Override
public void run() {
// update your JavaFX controls here
}
});

Related

JavaFX: Console Log in a TextArea + Multithreading and Tasks

I'm trying to achieve a feast which many attempted here on StackOverflow: showing the console output of a Java application in a TextArea built on JavaFX.
I've managed to show my output in said TextArea but, as many others, my UI freezes, 'cause this thread is heavily loading the one used to show the UI itself.
So I've started reading about Platform.runLater(), but it doesn't solve my issue, mostly because I'm outputting a lot of text and this slows down said function. Looking around, I've got into this question, where a nice solution based on Task is proposed. Neverthless, my UI keeps freezing as soon as I start to show my console log into the TextArea. I'll show you a snippet of my code, so that you may be able to tell me what I'm missing and/or doing wrong.
This is a snippet of my JavaFX controller:
public class MainViewController extends AbstractController implements Initializable {
#FXML private TextArea textAreaLog;
#Override
public void initialize(URL location, ResourceBundle resources) {
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
boolean fxApplicationThread = Platform.isFxApplicationThread();
System.out.println("Is call on FXApplicationThread: " + fxApplicationThread);
Console console = new Console(textAreaLog);
PrintStream ps = new PrintStream(console, true);
System.setOut(ps);
System.setErr(ps);
return null;
}
#Override
protected void succeeded() {
boolean fxApplicationThread = Platform.isFxApplicationThread();
System.out.println("Is call on FXApplicationThread: " + fxApplicationThread);
super.succeeded();
textAreaLog.textProperty().unbind();
}
};
textAreaLog.textProperty().bind(task.messageProperty());
new Thread(task).start();
}
// Console Class
public static class Console extends OutputStream {
private TextArea output;
Console(TextArea ta) {
this.output = ta;
}
#Override
public void write(int i) throws IOException {
output.appendText(String.valueOf((char) i));
}
}
}
I've edited the code taken from answer to the question I've previously linked, leaving all the debug messages just to help me out.
That's all. My UI just freezes, even if I'm apparently running my heavy-load task in the background instead of doing that directly in my UI thread.
I think root of the problem is one of the below;
System.out.println("text") is being a synchronized method.
accesing ui component outside of Ui thread
When you call System.out.println("text") from ui thread, the synchronization on System.out will cause UI to freeze for duration of synchronization.
You can check if this is the cause like below;(You have to wrap all your System.out calls like below, for only to test if the above theory is correct)
This will cause println methods to synchronize in different thread.(common-pool threads)
CompletableFuture.runAsync(()->System.out.println("text"));
You should also update output component in ui thread.(Problem is solved with this in this case)
// or create new runnable if you are not using java8
Platform.runLater(()->output.appendText(String.valueOf((char) i)));

toFront() throwing UnsupportedOperationException

I want to create a StackPane where, whatever is added, a node of my choice is always at the front (rather than having to be careful about the order I add things in, or remembering to call toFront() on that particular node whenever anything is added.)
In order to do this, I simply place a listener on the child list of the relevant StackPane object, so that whenever anything changes, it calls toFront() on the relevant node, for example:
public class Test extends Application {
#Override
public void start(Stage stage) {
StackPane root = new StackPane();
final Rectangle r1 = new Rectangle(50, 50);
root.getChildren().add(r1);
root.getChildren().addListener(new ListChangeListener<Node>() {
#Override
public void onChanged(ListChangeListener.Change<? extends Node> change) {
try {
while(change.next()) {
if(change.wasAdded()) {
r1.toFront();
}
}
}
catch(Exception ex) {
ex.printStackTrace();
}
}
});
root.getChildren().add(new Rectangle(50, 50));
stage.setScene(new Scene(root));
stage.show();
}
}
In Java 7, this works just fine. However, in JFX8 (latest build downloaded just now), it fails with the following:
java.lang.UnsupportedOperationException
at java.util.Collections$UnmodifiableList.add(Collections.java:1374)
at javafx.collections.ListChangeBuilder.nextRemove(ListChangeBuilder.java:208)
at javafx.collections.ObservableListBase.nextRemove(ObservableListBase.java:150)
at javafx.collections.ModifiableObservableListBase.remove(ModifiableObservableListBase.java:181)
at com.sun.javafx.collections.VetoableListDecorator.remove(VetoableListDecorator.java:284)
at com.sun.javafx.collections.VetoableListDecorator.remove(VetoableListDecorator.java:209)
at javafx.scene.Parent.impl_toFront(Parent.java:624)
at javafx.scene.Node.toFront(Node.java:1713)
at test.Test$1.onChanged(Test.java:34)
at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:315)
at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:72)
at com.sun.javafx.collections.VetoableListDecorator$1.onChanged(VetoableListDecorator.java:77)
at com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:315)
at com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:72)
at javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233)
at javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482)
at javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541)
at javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205)
at javafx.collections.ModifiableObservableListBase.add(ModifiableObservableListBase.java:155)
at java.util.AbstractList.add(AbstractList.java:108)
at com.sun.javafx.collections.VetoableListDecorator.add(VetoableListDecorator.java:200)
at test.Test.start(Test.java:41)
at com.sun.javafx.application.LauncherImpl$8.run(LauncherImpl.java:837)
at com.sun.javafx.application.PlatformImpl$7.run(PlatformImpl.java:331)
at com.sun.javafx.application.PlatformImpl$6$1.run(PlatformImpl.java:297)
at com.sun.javafx.application.PlatformImpl$6$1.run(PlatformImpl.java:294)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl$6.run(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.access$300(WinApplication.java:39)
at com.sun.glass.ui.win.WinApplication$4$1.run(WinApplication.java:112)
at java.lang.Thread.run(Thread.java:744)
And yes, test.Test$1.onChanged(Test.java:34) does indeed refer to r1.toFront();.
Is this to be considered a bug, or am I breaking some rule I'm unaware of by trying to achieve things this way? I did wonder whether the list was still being changed while the onChanged() method was executing, and toFront() would also change the list contents, hence the exception - but the Javadoc to onChanged() clearly says:
Called after a change has been made to an ObservableList.
(Bolding is mine.)
EDIT: At this point I'm more certain that it's a bug, so the related bug report is here.
It seems that you are not allowed to modify a (JavaFX) list inside an event handler that is currently handling another (previous) modification event of the same list. Although this seems reasonable it is not self-evident, so there should be a more obvious exception in that case.
Unfortunately non-speeking exceptions are very common in JavaFX.
Fortunately the solution/workaround is pretty easy: Call your modifying code (here: r1.toFront) by Platform.runLater(), it will delay your modification to happen after the originating event:
root.getChildren().addListener(new ListChangeListener<Node>() {
#Override
public void onChanged(ListChangeListener.Change<? extends Node> change) {
Platform.runLater(new Runnable() {
public void run() { r1.toFront(); }
});
}
});
Sidenote: toFront does nothing, if the component is already at front. This prevents infinite loops. Nevertheless, as this is not explicitely mentioned in the documentation, you might not rely on that.

NetBeans' HintsController and EventQueue

We are building an application based on the Netbeans Platform, and one part of it is an editor for a specific language we use.
We have the following class to highlight errors in the syntax:
class SyntaxErrorsHighlightingTask extends org.netbeans.modules.parsing.spi.ParserResultTask {
public SyntaxErrorsHighlightingTask () {
}
#Override
public void run (org.netbeans.modules.parsing.spi.Parser.Result result, org.netbeans.modules.parsing.spi.SchedulerEvent event) {
try {
final javax.swing.text.Document document = result.getSnapshot().getSource ().getDocument(false);
final List<ErrorDescription> errors = new ArrayList<ErrorDescription> ();
// finds errors on the document and add them to 'errors' list
}
/***
OFFENDING CODE GOES HERE
***/
} catch (javax.swing.text.BadLocationException ex1) {
org.openide.util.Exceptions.printStackTrace (ex1);
} catch (org.netbeans.modules.parsing.spi.ParseException ex1) {
Exceptions.printStackTrace (ex1);
}
}
#Override
public int getPriority () {
return 100;
}
#Override
public Class<? extends Scheduler> getSchedulerClass () {
return Scheduler.EDITOR_SENSITIVE_TASK_SCHEDULER;
}
#Override
public void cancel () {
}
}
The offending code, that throws an exception, is this:
org.netbeans.spi.editor.hints.HintsController.setErrors (document, "testsequence", errors);
Based on searching results, it was changed to the following:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
System.err.println("is EDT? " + SwingUtilities.isEventDispatchThread());
HintsController.setErrors (document, "testsequence", errors);
}
});
The following is what happens when a syntax error is introduced in the editor:
is EDT? true
SEVERE [org.openide.util.RequestProcessor]: Error in RequestProcessor org.netbeans.spi.editor.hints.HintsController$1
java.lang.IllegalStateException: Must be run in EQ
at org.netbeans.editor.Annotations.addAnnotation(Annotations.java:195)
at org.netbeans.modules.editor.NbEditorDocument.addAnnotation(NbEditorDocument.java:251)
at org.openide.text.NbDocument.addAnnotation(NbDocument.java:504)
at org.netbeans.modules.editor.hints.AnnotationHolder$NbDocumentAttacher.attachAnnotation(AnnotationHolder.java:235)
at org.netbeans.modules.editor.hints.AnnotationHolder.attachAnnotation(AnnotationHolder.java:208)
at org.netbeans.modules.editor.hints.AnnotationHolder.updateAnnotationOnLine(AnnotationHolder.java:674)
at org.netbeans.modules.editor.hints.AnnotationHolder.setErrorDescriptionsImpl(AnnotationHolder.java:899)
at org.netbeans.modules.editor.hints.AnnotationHolder.access$1300(AnnotationHolder.java:113)
at org.netbeans.modules.editor.hints.AnnotationHolder$4.run(AnnotationHolder.java:812)
at org.netbeans.editor.BaseDocument.render(BaseDocument.java:1409)
at org.netbeans.modules.editor.hints.AnnotationHolder.setErrorDescriptions(AnnotationHolder.java:809)
at org.netbeans.modules.editor.hints.HintsControllerImpl.setErrorsImpl(HintsControllerImpl.java:111)
at org.netbeans.modules.editor.hints.HintsControllerImpl.setErrors(HintsControllerImpl.java:93)
at org.netbeans.spi.editor.hints.HintsController$1.run(HintsController.java:79)
at org.openide.util.RequestProcessor$Task.run(RequestProcessor.java:1424)
at org.openide.util.RequestProcessor$Processor.run(RequestProcessor.java:1968)
Caused: org.openide.util.RequestProcessor$SlowItem: task failed due to
at org.openide.util.RequestProcessor.post(RequestProcessor.java:425)
at org.netbeans.spi.editor.hints.HintsController.setErrors(HintsController.java:77)
at com.#.#.#.editor.parser.SyntaxErrorsHighlightingTask$1.run(SyntaxErrorsHighlightingTask.java:74)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:641)
at java.awt.EventQueue.access$000(EventQueue.java:84)
at java.awt.EventQueue$1.run(EventQueue.java:602)
at java.awt.EventQueue$1.run(EventQueue.java:600)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:611)
at org.netbeans.core.TimableEventQueue.dispatchEvent(TimableEventQueue.java:148)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
[catch] at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)
What happens is, the call is to HintsController is being made in the EDT (EventDispatch Thread). However, Annotations.addAnnotation() is being run in another thread - sometimes in the "System clipboard synchronizer" thread, sometimes in the "Inactive RequestProcessor" thread. Since it checks if its being run on the EDT, it always throws an IllegalStateException.
I'm no expert in using the Netbeans Platform, and I'm pretty new to this specific application on the company - so I might be missing something really obvious. Google didn't help much. Anyone has any advice?
Turns out, it was not a problem with the code after all.
As pointed on the NetBeans-dev list:
HintsController.setErrors can be called from any thread - it uses its
own worker thread, and reschedules to AWT thread when necessary.
The requirement to invoke Annotations.addAnnotation in AWT thread has
been removed quite some time ago by:
http://hg.netbeans.org/main-silver/rev/db82e4e0fbcc
The same changeset also removed automatic rescheduling into AWT thread
in NbDocument.addAnnotation. So it seems that the build you are using
has the second part of the changeset, but not the first part (...)
After a careful review of maven's pom.xml files, I realized that the application was loading newer versions of the libs while the module was loading older versions, so it would run the wrong code. Related SO question about that here.
Maybe you should try to update your errors with a method like this one :
private void updateError(javax.swing.text.Document document, List<ErrorDescription> errors) {
if(javax.swing.SwingUtilities.isEventDispatchThread()) {
HintsController.setErrors (document, "testsequence", errors);
}
else {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
HintsController.setErrors (document, "testsequence", errors);
}
});
}
}

tomcat cannot reload context because of a background thread

I have a JSF2 project with Spring. It is developed on eclipse with tomcat attached to it. It is pretty straight forward and mostly with default settings.
But, we have a few background threads that look like this:
public class CrawlingServiceImpl implements CrawlingService, InitializingBean{
private final Runnable crawlingRunnable = new Runnable() {
#Override
public void run() {
//...
}
};
public void startCrawling() {
crawlingThread = new Thread(crawlingRunnable);
crawlingThread.start();
}
public void stopCrawling(){
if ( crawlingThread!=null )
crawlingThread.interrupt();
crawlingThread = null;
}
#Override
public void afterPropertiesSet() throws Exception {
startCrawling();
}
public void destroy(){
stopCrawling();
}
}
Here's who's calling the destroy() method:
<bean
id="crawlingService"
class="com.berggi.myjane.service.CrawlingServiceImpl"
autowire="byName"
scope="singleton"
destroy-method="destroy"/>
I know that there is a better way all this to be done. But this is not my code and I don't want to rewrite it.
My problem is the following:
When I change a class (every single time) or when I change an xhtml file (very rarely) the server attempts to reload it, but it fails with the following errors:
INFO: Illegal access: this web application instance has been stopped already. Could not load org.apache.xml.dtm.ref.DTMManagerDefault. The eventual following stack trace is caused by an error thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.
java.lang.IllegalStateException
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1562)
at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1521)
at org.apache.xml.dtm.ObjectFactory.findProviderClass(ObjectFactory.java:508)
...
at package.CrawlingServiceImpl.crawl(CrawlingServiceImpl.java:92)
at package.CrawlingServiceImpl$1.run(CrawlingServiceImpl.java:39)
at java.lang.Thread.run(Thread.java:680)
Note: Check the stacktrace. There are a lot of these exceptions.
Then there are more exceptions for a missing jdbc driver which is absolutely fine.
Any ideas?
are you sure crawlingThread.interrupt(); is killing the running of the Thread.
Without seeing the code of run(). it looks like it probably has a method called crawl and it does 1 of 2 things
1) a loop which expects a boolean variable to stop it running, and some possible sleeps/waits. I see an interupt, but no boolean being set to terminate the threads loop.
2) it runs once (no loop) and when finished dies - however, I don't see how the interupt will help here.
Assigning the Thread variable to null will not help to kill the thread.
if you want a quick fix, you could try set the thread to a daemon thread to allow it to be terminated.
private final Runnable crawlingRunnable = new Runnable() {
{
setDaemon(true);
}
#Override
public void run() {
//...
}
};
//...
}
But without the code, I'd guess the thread is refusing to die properly due to either issue 1 or 2.

JList setListData threading issues

I'm loading contents of a file to JList component. If I do the loading in main thread, everything seems to be OK - contents are loaded. But when I move loading code to separate thread, select an item in a list and try to reload the list, I get random NullPointer or IndexOutOfBounds exceptions. I'm sure this is some kind of Swing threading issue, but can't determine what.
This is my thread code:
#Override
public void run() {
List<String> textLines = null;
textLines = splitter.split(model.getLedMaxChars(), textLoader.loadText(file));
listener.onTextLoaded(textLines);//listener is in main Swing code
}
Controller is responsible for listening:
#Override
public void onTextLoaded(List<String> textLines) {
view.fileLoaded(model.getCurrentFile());
view.setTextLines(textLines);
view.enableListComponent();
}
And the view updates:
public void setTextLines(List<String> textLines) {
jList.setListData(textLines.toArray());
}
I've tried to leave thread to hang by adding while(true); loop - then everything works OK. If I hit reload without selecting item in a list, everything works too.
Could anyone explain what I'm missing here?
Swing components are usually not thread-safe. This means that only the Swing worker thread should do any modifications:
Runnable worker = new Runnable() {
public void run() {
jList.setListData(textLines.toArray());
}
};
SwingUtilities.invokeLater(worker);
See also:
http://java.sun.com/products/jfc/tsc/articles/threads/threads1.html
http://java.sun.com/products/jfc/tsc/articles/threads/threads2.html
Swing is not thread safe, so when you are manipulating GUI elements from other threads many weird things may occur. In your case the simplest solution (but probably not the best) would be to use SwingUtilities.invokeLater

Categories