A proper way to output text immediately in Java Swing GUI - java

Such a piece of code:
private void log(String message) {
LogBox.append(message + "\n");
}
private void log(Exception e) {
log(e.getMessage());
}
private void ConvertButtonActionPerformed(java.awt.event.ActionEvent evt) {
String url = UrlBox.getText();
if (url.isEmpty()) {
log("Empty URL");
return;
}
LogBox.setText("");
try {
log("URL "+url+" accepted. Trying to download...");
String content = URLConnectionReader.getText(url);
log("Downloaded. Parsing the content...");
//
} catch (Exception e) {
log(e);
}
}
should output each message to the LogBox (JTextArea) immediately after each log call, but outputs URL ... accepted only when URLConnectionReader.getText(url) finishes.
There were several ways do do an immediate output:
Application.DoEvents in Visual Basic 6 and .NET
Application.ProcessMessages in Delphi
Is there some simple way to do an immediate output? I was studying questions about the DoEvents and how to do this in Java, but I think that starting to learn Java from multi-threading isn't a right approach.

Create a SwingWorker to do the download: http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html
The role of an ActionListener is just that: to listen for user action, and initiate a response to that action by the program.
Sometimes, the program's response is very quick, and only involves the GUI. For example, in a calculator app, you could have a listener attached to an "equals" button that calculates the result of the current expression and writes it to a textbox. This can all be done within the listener method (although you might want to separate behavior for testing).
If the response to an user action is to initiate some long-running process, like downloading and parsing the file, then you don't want to do this within the listener body, because it will freeze the UI. Instead, gather any information (in your case, the URL value) from within the listener, and spin up a SwingWorker to handle the program's response.
In my comment, I suggested moving everything after the getText() into a SwingWorker. This is because, to me, the response is "download a file if you have a valid URL, and log the progress." And as I see it, testing for an empty string is part of that response. If you want to leave the empty-string test inside the listener, that's fine, but imo it's less testable.
You must leave the call to getText() inside the body of the listener, because you are only allowed to access Swing components from the event dispatch thread. If you moved that call into the worker, then it might access the textbox at the same time the textbox is updating itself, resulting in corrupt data.

Read up on Concurrency.
You should probably use a SwingWorker for the long running task, then you can publish results to the GUI as they become available.

Related

How to set a Java Gui listener for unexpected program end

I am making a personal planning program. I utilize XML documents to store user data and login data. This does not utilize a server and all accounts created in the program are localized to the computer it is stored on (in the XML documents.)
In the login XML document, I keep track of the users that are logged in so that one user can't have two windows at the same time to prevent any conflicts with data. This feature runs smoothly and I have no problem with it.
The only thing I want to know is if there is some way to catch an unexpected shut down of a program (such as a task-manager close or a forced close when shutting down the computer) so that I can "log" the user off of the XML document. Otherwise the user would never be able to get back on after an unexpected program close without going into the XML document and deleting the username from the logged in list.
It seems a shutdown hook does not work well with the event queue for a java GUI. much like this thread
I tried setting up my code exactly as shown and the shutdown hook doesn't work for me either. Are there any suggestions for ways of catching an unexpected shutdown without shutdown hooks?
this is my code:
import java.awt.EventQueue;
public class Gui {
private static Controller controller;
public static void main (String[] args) {
controller = new Controller();
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
controller.saveState();
controller.logUserOut();
}
});
EventQueue.invokeLater(
new Runnable() {
public void run() {
controller.start();
}
});
}
}
This is a closer look at my controller that logs the user out
public void logUserOut() {
loginDatabase.logUserOut(username);
saveLoginState();
}
All loginDatabase does is removes that username from the list of logged in users so that user is free to log in again
public void saveLoginState() {
XStream xStream = new XStream(new DomDriver());
OutputStream outFile;
try {
String filePath = "data" + File.separator + "loginDatabase.xml";
outFile = new BufferedOutputStream(new FileOutputStream(filePath));
xStream.toXML(loginDatabase, outFile); // This writes your state to the outputFile;
outFile.close(); //close the writer
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
This is my process for writing on the login state xml file. I suspect it might be too long for a Shutdown Hook even if it were actually being called as I expect.
Any suggestions? I thought for a long time about possibly using simple variables to solve the problem but because I have the program set so that the user can be logged into multiple accounts, the use of variables is impossible.
Also, will the controller object contained in the scope of the shutdown hook be the same controller that is modified in the event queue scope?
The shutdown hook can be a solution here. For details see e.g. this answer: https://stackoverflow.com/a/2541618/2045440
[Off topic] However, if you're at risk that unexpected termination of your application can result in lost of important data, maybe it would be worth to consider the more persistent way of processing this data (autosaving, backup files etc).
I resolved the problem by checking the task manager of the operating system. It allowed me to see if the user the xml document said was logged in actually had a program open. If not then I knew there was a problem. This is a fix for my particular program but it might be a help for others as well.
The link to that thread is here.

How do I update the appearance of my JButton when it's clicked?

I'm working on a Java7 Swing "wizard" type of project that needs to validate a web address before continuing on to the next step of the wizard. The validation requires accessing a URL over the internet to verify that expected resources are available. In some cases, this can take a few seconds which can be long enough to confuse a user.
As a quick solution to this, I would like to disable the "next" button and change the display text while the validation is running so the user knows that the wizard is working and not hung up on anything. The problem is that when I add the code to modify the JButton, none of the changes happen until after the validation has completed. This is the case even if I change the button and call revalidate() before I execute the validation methods.
Here is an excerpt of what I've tried:
// create next button
next = new JButton("Next", new ImageIcon(getClass().getResource("/navigate_right.png")));
next.setActionCommand("MYACTION");
next.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
System.out.println("Is this the event dispatch thread? "
+ javax.swing.SwingUtilities.isEventDispatchThread());
System.out.println("Changing button");
next.setEnabled(false);
next.setText("Testing Connection");
next.getRootPane().revalidate();
System.out.println("Validating Service");
boolean isValidLocation = ServiceValidator.serviceExists(locationField.getText());
// ...etc...
When I run this code, the lines "Changing button" and "Validating Service" both get printed before the actual button changes in the display. How can I get the JButton to change before System.out.println("Validating Service"); is printed?
The problem is that when I add the code to modify the JButton, none of the changes happen until after the validation has completed.
Your code is executing on the EDT, so you long running code prevents the GUI from repainting itself until the task is finished executing. You need to use a separate Thread for the long running task, maybe a SwingWorker. Read the section from the Swing tutorial on Concurrency for more information.

Delay when coding in Java Swing

I am writing an GUI application using Java Swing. I just have 1 problem which is that I wrote a big function that gets executed when clicking a button. At first I am setting the value of a label to "generating report.." then just before the 'return' part of the function I set the value of the label "done generating report". What is actually happening is that the first setting of the label doesn't take effect. They both take effect after I am done with the function call so the end result just shows 'done generating report'.
Here is the java code I am using:
protected void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jButton2ActionPerformed
// TODO add your handling code here:
try
{
jLabel6.setText("Generating Report");
jProgressBar1.setVisible(true);
generateReport(BillImportId.getText()); // This will take around 30 seconds to finish
}
catch (Exception e)
{
System.out.println("Exception occured: " + e);
}
}//GEN-LAST:event_jButton2ActionPerformed
Move the call to generateReport into the method doInBackground of a SwingWorker. Use the methods process and done to update your UI elements.
Check out also "Concurrency in Swing"

Java swing GUI components not showing up after Observer update() notified from a separate thread

My application basically uses apache's httpclient to connect to a server and do something for the user.
I am a java beginner (though not a development beginner), so many java specific approaches may have been calmly ignored.
DESCRIPTION
the app uses a TrayIcon with a popup menu as the only user interface and reacts with either tray messages or popup [confirm] dialogs.
it uses threads because the httpclient was effectively blocking the main thread when waiting for a response
employs a client-server to assure a single instance only and reactions to second instance attempts
employs observer/observable pattern to react to the httpclient's and the single instance watchdog server's messages
the main behavior is controlled by a Controller class, which is the only Observer; everything related to the httpclient is in a separater WebClient class, which implements Runnable and extends Observable; then there's the AttServer which is again Runnable and Observable and controls everything around the server listener (there's not much actually).
This is my first application in Java, so many obscurities are possible..:(
PROBLEM
The desired behavior is that whenever a user attempts to run a second instance of this app, then it tries to bind a socket listener on port X and, failing, sends a string message to that port. The 'main' app accepts the message and reacts with a confirm dialog being shown asking the user for some input.
Everything was working just fine up to the point when I've added the client-server; the second instance successfully triggers the update() method of the observer, the observer correctly distinguishes what's going on and fires the corresponding method; and then at the point of the first new GUI component (a confirm dialog) should show up, NOTHING happens:
the app doesn't do anything
no errors or exceptions thrown in System.out
the tray icon CONTINUES to work just fine (right click shows a menu etc)
..it's just that all the instructions coming after the dialog doesn't get executed and the dialog doesn't show up.
WEIRDNESS #1
When the user uses the tray icon menu to fire some other command that shows a dialog, the GUI somehow coughs up and shows BOTH (or more when appropriate) dialogs in the correct order (first the stuck hidden dialog shows up and then the second fresh one) and everything just seems that this is the correct state and all commands get executed correctly (albeit some of them 'a bit' later).
WEIRDNESS #2
I guess this is caused by the swing being not really compatible with threads; however, when the update() method gets fired up from the httpclient (which is a separate thread as well), the GUI works just fine. The only difference in the way the Observer gets notified is that the WebClient calls the notifyObservers() after some user action (albeit in a separate thread and after (at worst) 30s timeout and the AttServer calls notifyObservers() in a catch() block, because IOException is thrown when the specified port has been already bound.
WEIRDNESS #3
This problem is irregular to say the least. It USUALLY occurs in the development environment (netbeans), although sometimes it doesn't --- at first I've got the impression it was solved by calling another (dummy) JDialog, which remained hidden intentionally, before the actual problematic dialog --- it seemed that swing needed a little 'nudge outta the door'; though after building the project into a jar or an exe (via launch4j), the problem was back. Then I've tried to use a single JFrame component for ALL the swing dialogs and it helped somehow --- sometimes when the output jar is run it works as it should and the next day it hides dialogs again.
I am totally lost, scoured the Earth for a solution, found nothing really. I hope that somebody else might bump into some similar problem and, being a more experienced Javaman, found a solution. I've also tried some doLayout(), validate(), revalidate(), repaint(), update() calls on the main JFrame instance, but no go.
There's hope :) Thanks for reading and thanks even more for any ideas.
CODE EXAMPLES
This is the run() function from AttServer -- the source of the non-working update()s.. (it's a bit hard coded as the main functionality is there and I got stuck elsewhere..)
public void run() {
this.addObserver( Controller.getInstance() );
setChanged();
// this.command == INIT set and new thread with AttServer called in the beginning of the main()
if( null != this.command ) {
switch( this.command ) {
case INIT:
try {
init();
} catch( IOException ex ) {
notifyObservers( new ErrorEvent( 100 , "Could not bind the specified port." ) );
}
break;
default:
} //switch
// no command, looks like an incoming connection
} else {
try {
DataInputStream in = new DataInputStream( socket.getInputStream() );
String inputLine;
while( ( inputLine = in.readLine() ) != null ) {
if( inputLine.trim().equals( EventType.CLOCKIN.toString() ) ) {
notifyObservers( EventType.CLOCKIN );
return;
}
}
} catch( IOException ex ) {
//todo
}
}
}
..this is the opposite side of the fence, the update() method:
public void update( Observable obj, Object arg) {
// received a remote request *******************************************
if( obj instanceof AttServer
&& arg instanceof EventType ) {
EventType command = (EventType) arg;
Attendance.debugMsg( "Received " + command.toString() + " request from AttServer" );
if( false == isOnClock ) {
doClock( true , true );
}
} // ..........
.. and this is the essence of the doClock() method called from update() -- there's nothing really, and even when the dialog is shown right after the call, or event when it's shown right in the update() method, it doesn't work.
(overloaded with default data)
public static void doClock( Boolean clockIn , Boolean confirm , String message , String title ) {
Event lastEvent = Log.getLastClock();
if( confirm ) {
if( JOptionPane.NO_OPTION == JOptionPane.showConfirmDialog( mainFrame , message , title , JOptionPane.YES_NO_OPTION ) ) {
return;
}
}
Without reading through this whole document (!!!) I would guess that you are not updating the GUI via the Event Dispatch Thread (EDT).
The GUI is supposed to be updated only by a single thread, the EDT.
You can have background threads in your applications, but the update of the GUI must be passed to be done to the EDT only.
Do not touch any Swing control in a thread of your own i.e. other than EDT.
Otherwise you will have tons of problems.
It is plainly wrong not to go through the EDT

How to wait for an attribution in startup() to complete before continuing?

I'm making a desktop application which watches a folder using watchservice from java.nio.file . But I need the gui to be loaded before I start watching, because the path to be watched is in a JFieldText on the UI.
public class FileArchiverApp extends SingleFrameApplication {
static FileArchiverView gui;
#Override protected void startup() {
gui = new FileArchiverView(this); //HERE0 I have to wait for this.
show(gui);
...
public static void main(String[] args) throws IOException {
launch(FileArchiverApp.class, args);
....
WatchService watcher = FileSystems.getDefault().newWatchService();
// HERE1 while(gui==null) System.out.println("hi") ;
try {
Path dir = Paths.get(gui.getOriginPath()); // HERE2 I get nullpointer if gui was not ready
WatchKey key = dir.register(watcher, ENTRY_CREATE );
} catch ( Exception x) {
System.err.println(x);
}
while(true){ /*wait for new file event loop*/ }
}
The function getOriginPath() returns the getText() form the text field I mentioned.
In HERE0 is the attribution I mentioned. I get a nullpointer in HERE2 if gui wasn't ready.
I've tried things. If I put that thing in HERE1 it works, but of course I don't want to do that.
How could I make it?
And its taking to long(like two seconds) or the gui to stop being null with this HERE1 I don't know if it is because of the println, but I was expecting it to be almost instantaneous. Is it normal?
Thanks.
Given the limited information posted, I have to make some assumptions. Assumption 1 is that you give the JTextField a default value and use that as the path to the file you wish to watch. Assumption 2 is that you have not coded with an eye towards MVC-like design.
If both are correct, then it sounds like you have the tail wagging the dog -- the view holding the critical data, not the model. Why not fix your problem by going towards MVC and not getting the critical data from the view but rather from the model. Start the model up first thing, including getting the default path from your program Properties, get your listener going, start your view, and then if the view asks the controller to change the watched file, have the controller change the model. And then listeners in the model will notify your any observers of change.

Categories

Resources