Skip to Main Content

Java SE (Java Platform, Standard Edition)

Announcement

For appeals, questions and feedback about Oracle Forums, please email oracle-forums-moderators_us@oracle.com. Technical questions should be asked in the appropriate category. Thank you!

Thoughts on best practices for busy/wait and threads?

zonskiOct 12 2011 — edited Nov 4 2011
Hey Guys,

I have code working for doing busy/wait (i.e. blocking the UI and showing a 'wait' indicator while the system is doing some slow job such as connecting to a server). I'm posting it here partly as a reference as there is not a lot of good (any?) docco on using Services and Workers in the official docco, but also to get feedback from you guys on where this could be improved from a design/architecture point of view.

Below is a very simple controller that is hooked up to a Login form (defined in FXML). The controller takes a username and password and tries to login (currently hardcoded with a sleep but in the real world I would connect to a remote server using Spring+HttpInvoker or the like).

I am not overly happy with all the inner classes nor the error handling stuff. It all seems a bit convoluted and heavy for my simple scenario. I have used a Service (man I wished they'd not used that name! now what do I call all my remote service objects?) to do my work as I want to be able to cancel and restart it if needed. Services are designed to be reused but I have no need of this (my login controller has sole responsibility for managing logins) so I'm feeling like a Service is a bit of overkill.

I use a roll-my-own glasspane (defined in the FXML) with a 'wait' cursor to block the UI and indicate that work is happening. The FXML is below, but this is just rough and I'm not worried about the aesthetics of this (that's another topic!) - the focus is on the controller and the threading.

I'm really interested in what people think, what other people are doing, or thinking of doing, and just general feedback.


CONTROLLER
package com.zenjava.javafx;

import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.PasswordField;
import javafx.scene.control.TextField;

import javax.naming.AuthenticationException;
import java.net.URL;
import java.util.ResourceBundle;

public class LoginController implements Initializable
{
    @FXML private TextField userNameField;
    @FXML private PasswordField passwordField;
    @FXML private Label messageLabel;
    @FXML private Node glassPane;

    private SimpleBooleanProperty busy;
    private LoginService loginService;

    public void initialize(URL url, ResourceBundle resourceBundle)
    {
        busy = new SimpleBooleanProperty();
        glassPane.visibleProperty().bind(busy);
    }

    @FXML public void login(ActionEvent event)
    {
        if (loginService == null)
        {
            loginService = new LoginService();
        }
        messageLabel.setText(null);
        loginService.reset();
        loginService.start();
    }


    //-------------------------------------------------------------------------

    public class LoginService extends Service<String>
    {
        public LoginService()
        {
            busy.bind(runningProperty());

            stateProperty().addListener(new ChangeListener<State>()
            {
                public void changed(ObservableValue<? extends State> source, State oldState, State newState)
                {
                    if (newState.equals(State.SUCCEEDED))
                    {
                        // do success (in reality this would change the display to the main screen)
                        messageLabel.setText("Login succeeded");
                    }
                    else if (newState.equals(State.FAILED))
                    {
                        Throwable exception = getException();
                        if (exception instanceof AuthenticationException)
                        {
                            messageLabel.setText("Invalid username or password");
                        }
                        else
                        {
                            messageLabel.setText("Oh no, system error, we're all going to die!");
                            exception.printStackTrace();
                        }
                    }
                }
            });
        }

        protected Task createTask()
        {
            final String userName = userNameField.getText();
            final String password = passwordField.getText();
            return new Task<String>()
            {
                protected String call() throws Exception
                {
                    // ordinarily would be a remote service call - hardcoded for now

                    Thread.sleep(1000); // fake a small delay to simulate a server call
                    if (userName.equals("zonski") && password.equals("password"))
                    {
                        return "Dan Z";
                    }
                    else
                    {
                        throw new AuthenticationException("Invalid username or password");
                    }
                }
            };
        }
    }
}
FXML
<?fxml version="1.0" encoding="utf-8"?>

<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>


<StackPane xmlns:fx="http://javafx.com/fxml" fx:controller="com.zenjava.javafx.LoginController">

    <children>
        <FlowPane alignment="CENTER">
            <children>
                <VBox style="-fx-border-color: gray; -fx-padding: 10">
                    <children>
                        <Label text="Login"/>

                        <Label fx:id="messageLabel"/>

                        <Label text="Username"/>
                        <TextField fx:id="userNameField"/>

                        <Label text="Password"/>
                        <PasswordField fx:id="passwordField"/>

                        <Button text="Login" onAction="#login"/>
                    </children>
                </VBox>
            </children>
        </FlowPane>

        <BorderPane fx:id="glassPane" visible="false" style="-fx-cursor: wait"/>

    </children>

</StackPane>
Cheers,
zonski
Comments
Locked Post
New comments cannot be posted to this locked post.
Post Details
Locked on Dec 2 2011
Added on Oct 12 2011
3 comments
1,535 views