• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Using a JTextArea as a console

 
Ranch Hand
Posts: 46
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I want to use a JTextArea as a console, ie a read-only scrolling text report. It's visible in the UI but written to, a line at a time, from a background thread so I'm using SwingUtilities.invokeLater to add the text in the EDT. Because the background task generates text rapidly, I'm batching up the String until such time as Swing gets round to processing it.

It works most of the time, but occasionally just stops updating, as though the run() never ran: appendage just keeps growing. By copying appendage to append before using it, I expected it to fail by very rarely missing lines of text rather than by apparently missing the invokeLater().

 
Bartender
Posts: 732
10
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
First, don't use the name "append" for the argument to your appendText method - too easy to confuse it with the "append" method inherited form JTextArea.
So let's call it ":textToAdd".
Then the appendText methos should just call 'append(textToAdd)' if executed from the EDT.
If not on the EDT, call invokeLater with the new runnable, which should have textToAdd as an argument to the runnable's constructor.
Then appendText calls showEnd().

The runnable's constructor saves the argument as "newText", and the run() method just calls appendText(newText).
No need for the variable 'appendage' at all.
 
Malcolm Storey
Ranch Hand
Posts: 46
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks for your thoughts. That's pretty well what I had originally, but there were multiple calls coming thru much faster than invokeLater can handle them using separate calls for each chunk of text (and I'm not sure they're guaranteed to be processed in the right order).

So variable appendage is used to accumulate the text: I was trying to append the additional lines of text to the queue. Perhaps I need to use a threadsafe queue of append Strings, rather than simply appending to the queued String, tho Strings are atomic so I thought it should work.
 
Author
Posts: 986
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
AbstractDocument has an insertString() method that is documented to be thread-safe, and I think that using it is probably the way to go.

So what you do is, in the EDT, save a reference to the underlying Document.
Document doc = myJTextArea.getDocument();
assert( doc instanceof AbstractDocument ); // optional (It will be, unless you're doing something unusual.)

Then just call
doc.insertString(doc.getLength(), stringToBeInserted, null);
from arbitrary threads and things should be fine.

Now the thread-safety doesn't come free. The insertString() method will acquire a write lock, which in some circumstances can be slow. If the text is coming fast and furious then it might make sense to do some batching. But I'd try it without first and see how it performs.
 
Brian Cole
Author
Posts: 986
3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Brian Cole wrote:Then just call
doc.insertString(doc.getLength(), stringToBeInserted, null);
from arbitrary threads and things should be fine.



Actually, getLength() is not documented to be thread-safe in AbstractDocument, so this might require some care. Since you have a single background thread that's generating the text, it sounds like you should be able to manage.
 
Malcolm Storey
Ranch Hand
Posts: 46
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thanks guys, that works nicely. I still need the invokeLater for showEnd but I can use an "inFLight" flag to omit redundant calls.

Returning to
doc.insertString(doc.getLength(), stringToBeInserted, null);
This has two (hopefully) threadsafe calls but the content might change between them. It's a shame Java doesn't have a simple syntax for marking a block of code as atomic. This crops up so often!

Thanks again,
Malcolm



 
reply
    Bookmark Topic Watch Topic
  • New Topic