Well, it really isn't a big pain: but I fear of security risks (if that is even possible).Background:I decided to (sort of) abandon my Sudoku project (because I accidentally deleted it from disk), and was given the idea of a JavaFX Helper Library. I started with the LoginPane, as I find I use login popups quite often.The code is here:import javafx.geometry.HPos;import javafx.geometry.Insets;import javafx.geometry.Pos;import javafx.scene.Scene;import javafx.scene.control.Button;import javafx.scene.control.Label;import javafx.scene.control.TextField;import javafx.scene.layout.ColumnConstraints;import javafx.scene.layout.GridPane;import javafx.scene.layout.HBox;import javafx.stage.Stage;public class LoginPane {    public static final String DEFAULT_TITLE = Login;    public static final String USERNAME_LABEL_DEFAULT_TEXT = Username: ;    public static final String PASSWORD_LABEL_TEXT = Password: ;    public static final String DEFAULT_LOGIN_TEXT = Login;    public static final String CANCEL_TEXT = Cancel;    public static final boolean DEFAULT_HAS_CANCEL = true;    public static final boolean DEFAULT_CAN_CLOSE = true;    private static final Insets MAIN_PANE_PADDING = new Insets(10);    private static final int MAIN_PANE_GAP = 10;    private static final int BUTTON_PREF_WIDTH = 60;    private static final int BUTTON_PANE_SPACING = 10;    private static final boolean IS_RESIZABLE = false;    private static final ColumnConstraints COL_1_CONSTRAINS = new ColumnConstraints(            70);    private static final ColumnConstraints COL_2_CONSTRAINS = new ColumnConstraints(            200);    private static String username = null;    private static char[] password = null;    public static UserInfo showLoginPane() {        return showLoginPane(DEFAULT_TITLE);    }    public static UserInfo showLoginPane(String title) {        return showLoginPane(title, USERNAME_LABEL_DEFAULT_TEXT);    }    public static UserInfo showLoginPane(String title,            String usernameLabelText) {        return showLoginPane(title, usernameLabelText, DEFAULT_LOGIN_TEXT);    }    public static UserInfo showLoginPane(String title,            String usernameLabelText, String loginText) {        return showLoginPane(title, usernameLabelText, loginText, DEFAULT_HAS_CANCEL, DEFAULT_CAN_CLOSE);    }    public static UserInfo showLoginPane(String title,            String usernameLabelText, String loginText, boolean hasCancel,            boolean canClose) {        Stage stage = new Stage();        GridPane mainPane = new GridPane();        mainPane.setPadding(MAIN_PANE_PADDING);        mainPane.setHgap(MAIN_PANE_GAP);        mainPane.setVgap(MAIN_PANE_GAP);        mainPane.getColumnConstraints().addAll(COL_1_CONSTRAINS,                COL_2_CONSTRAINS);        Label userLabel = new Label(usernameLabelText);        GridPane.setHalignment(userLabel, HPos.RIGHT);        TextField usernameField = new TextField();        GridPane.setHalignment(usernameField, HPos.LEFT);        mainPane.addRow(0, userLabel, usernameField);        Label passwordLabel = new Label(PASSWORD_LABEL_TEXT);        GridPane.setHalignment(passwordLabel, HPos.RIGHT);        PasswordField passwordField = new PasswordField();        GridPane.setHalignment(passwordField, HPos.LEFT);        mainPane.addRow(1, passwordLabel, passwordField);        HBox buttonPane = new HBox(BUTTON_PANE_SPACING);        Button login = new Button(loginText);        login.setPrefWidth(BUTTON_PREF_WIDTH);        buttonPane.getChildren().add(login);        login.setOnAction(e -> {            username = usernameField.getText();            password = passwordField.getText().toCharArray();        });        if (hasCancel) {            Button cancel = new Button(CANCEL_TEXT);            cancel.setPrefWidth(BUTTON_PREF_WIDTH);            buttonPane.getChildren().add(cancel);            cancel.setOnAction(e -> stage.close());        }        buttonPane.setAlignment(Pos.CENTER_RIGHT);        mainPane.add(buttonPane, 1, 2);        GridPane.setHalignment(buttonPane, HPos.RIGHT);        Scene scene = new Scene(mainPane);        stage.setTitle(title);        stage.setScene(scene);        stage.setResizable(IS_RESIZABLE);        stage.setOnCloseRequest(e -> {            if (canClose) {                stage.close();            } else {                e.consume();            }        });        stage.showAndWait();        return new UserInfo(username, password);    }}and if you are wondering what UserInfo is, it's here:import java.util.Arrays;public class UserInfo {    private final String username;    private final char[] password;    public UserInfo(String username, char[] password) {        this.username = username;        this.password = password;    }    public String getUsername() {        return username;    }    public char[] getPassword() {        return Arrays.copyOf(password, password.length);    }}Snapshot (showLoginPane() with no arguments):Explanation:The showLoginPane() method is the basic method. It provide default values for all the possible editable values. The editable values are as follows:String title: the title of the popup.String usernameLabelText: the text that replaces the default Username: .String loginText: the text that replaces the default Login text on the button.boolean hasCancel: if true, the cancel button is added. If false, then the cancel button is not shown.boolean canClose: if true, the popup is closeable through the X button. Otherwise, it will not close.Questions:Is the static method, constants, and return values username and password good practice? I couldn't find a way around this.Which constants should be public, and which private?Naming?Anything else?
This LoginPane is a Pain
java;authentication;javafx
ConstantsAll the public statics should be private. If clients really care, you can document the default label, but then you can't change the label without it being an API spec change. I wouldn't expect most clients need to know the default. Either they care to specify or they don't.For the privates, my personal preference would be to inline all the ones that will only ever be used in one place, but I can see the argument for extracting them.DesignI would suggest making a LoginPane an instantiable class, rather than just a helper method. Conceptually, a LoginPane is a thing, and so representing instances as objects would seem to be reasonable. It would have a constructor and a public UserInfo show() method, which would make it reusable.You should also consider using the Builder pattern to avoid the telescoping static method problem that you have. It would allow clients to specify only the values they care about. Documentation can indicate defaults - a good idea for the booleans, at least, though per above the labels are debatable.NamingFor consistency, canCancel would be better than hasCancel. The documentation can indicate that the cancel option is only made available if canCancel is true. Non-abbreviated names are generally better - COLUMN_1_CONSTRAINTS > COL_1_CONSTRAINTS.CorrectnessThe passwordLabel and cancelButton don't use the non-default label values, even if they're provided as method arguments. I'm not sure how much making the password a char[] actually helps you, because it's already in memory as a String from getText(). I'm not a JavaFX expert, so I'm not sure if there's a more correct way to do it, but a quick search implies there is not.You have a bug at the intersection of canClose and hasCancel. If hasCancel is true and canClose is false, you render a cancel button which does nothing.You also don't differentiate at all between somebody hitting [Ok] without entering any values and somebody hitting [Cancel]. It's unclear if that's relevant to clients, but probably it is.Here's something I slapped together that addresses many of the issues I discussed. public final class LoginPane {    private static final Insets MAIN_PANE_PADDING = new Insets(10);    private static final int MAIN_PANE_GAP = 10;    private static final int BUTTON_PREFERRED_WIDTH = 60;    private static final int BUTTON_PANE_SPACING = 10;    private static final boolean IS_RESIZABLE = false;    private static final ColumnConstraints COLUMN_1_CONSTRAINS = new ColumnConstraints(70);    private static final ColumnConstraints COLUMN_2_CONSTRAINS = new ColumnConstraints(200);    private final String username = ;    private final char[] password = new char[0];    private final Stage stage = new Stage();    private LoginPane(final Builder builder) {        final GridPane mainPane = new GridPane();        mainPane.setPadding(MAIN_PANE_PADDING);        mainPane.setHgap(MAIN_PANE_GAP);        mainPane.setVgap(MAIN_PANE_GAP);        mainPane.getColumnConstraints().addAll(COLUMN_1_CONSTRAINS, COLUMN_2_CONSTRAINS);        final Label userLabel = new Label(builder.usernameLabel);        GridPane.setHalignment(userLabel, HPos.RIGHT);        final TextField usernameField = new TextField();        GridPane.setHalignment(usernameField, HPos.LEFT);        mainPane.addRow(0, userLabel, usernameField);        final Label passwordLabel = new Label(builder.passwordLabel);        GridPane.setHalignment(passwordLabel, HPos.RIGHT);        final PasswordField passwordField = new PasswordField();        GridPane.setHalignment(passwordField, HPos.LEFT);        mainPane.addRow(1, passwordLabel, passwordField);        final HBox buttonPane = new HBox(BUTTON_PANE_SPACING);        final Button login = new Button(builder.loginText);        login.setPrefWidth(BUTTON_PREFERRED_WIDTH);        buttonPane.getChildren().add(login);        login.setOnAction(e -> {            username = usernameField.getText();            password = passwordField.getText().toCharArray();        });        if (builder.canCancel) {            final Button cancel = new Button(builder.cancelText);            cancel.setPrefWidth(BUTTON_PREFERRED_WIDTH);            buttonPane.getChildren().add(cancel);            cancel.setOnAction(e -> stage.close());        }        buttonPane.setAlignment(Pos.CENTER_RIGHT);        mainPane.add(buttonPane, 1, 2);        GridPane.setHalignment(buttonPane, HPos.RIGHT);        final Scene scene = new Scene(mainPane);        this.stage.setTitle(builder.title);        this.stage.setScene(scene);        this.stage.setResizable(IS_RESIZABLE);        this.stage.setOnCloseRequest(e -> {            if (builder.canCancel || builder.canClose) {                stage.close();            } else {                e.consume();            }        });    }    public UserInfo showAndWait() {        this.stage.showAndWait();        return new UserInfo(this.username, this.password);    }    public static final class Builder {        private String title = Login;        private String usernameLabel = Username: ;        private String passwordLabel = Password: ;        private String loginText = Login;        private String cancelText = Cancel;        private boolean canCancel = true;        private boolean canClose = true;        public Builder() {        }        public Builder title(final String title) {            this.title = title;            return this;        }        public Builder usernameLabel(final String usernameLabel) {            this.usernameLabel = usernameLabel;            return this;        }        public Builder passwordLabel(final String passwordLabel) {            this.passwordLabel = passwordLabel;            return this;        }        public Builder loginText(final String loginText) {            this.loginText = loginText;            return this;        }        public Builder cancelText(final String cancelText) {            this.cancelText = cancelText;            return this;        }        /**         * Whether or not the user can cancel this login dialog without entering values. True by default.         * @param canCancel if true, the user can cancel the login dialog without entering values.         * @return the instance of this Builder for method chaining. Will never return null.         */        public Builder canCancel(final boolean canCancel) {            this.canCancel = canCancel;            return this;        }        public Builder canClose(final boolean canClose) {            this.canClose = canClose;            return this;        }        public LoginPane build() {            return new LoginPane(this);        }    }}Using it might look something like:final LoginPane loginPane = new LoginPane.Builder().title(New Title).canCancel(false).build();loginPane.showAndWait();If you wanted to be really fancy, you could add public static methods to the LoginPane class which create and return a Builder with that value set. Then the client call would look more like:final LoginPane loginPane = LoginPane.title(New Title).canCancel(false).build();loginPane.showAndWait();It makes the code a bit easier to read at the cost of a slightly messier API.