View Part 2 - Validate Run Mode OptionsJ8 Home « View Part 2 - Validate Run Mode Options

In our final section on coding the case study we complete the view code elements. We start the section by coding the RunModeDialog class which creates a dialog box to accept and validate run mode options for the Manufacturer application.

Validate Run Mode Options Top

In the first part of the lesson we code up the RunModeDialog class which creates a dialog box to accept and validate run mode options for the Manufacturer application. The class provides a standard dialog box which allows the user to select the location of the file (which may be a physical file in NON_NETWORK_CLIENT mode, or the address (and, optionally, the port) of the server for NETWORK_CLIENT mode. The class implements the observer class which means it wants to be informed of changes in observable objects, which in our case is changes to the values in the panel of the RunModeOptions class.

Creating The RunModeDialog Class Top

The RunModeDialog class creates a dialog box to accept and validate run mode options for the Manufacturer application. This class provides a standard dialog box which allows the user to select the location of the file (which may be a physical file in NON_NETWORK_CLIENT mode, or the address (and, optionally, the port) of the server for NETWORK_CLIENT mode. This class implements the Observer interface. What this means is that class will be notified whenever changes are made within the run mode options panel of the RunModeOptions class, as that class has an inner class that extends the java.util.Observable class; which represents an observable object, or "data" in the model-view paradigm.

These classes are based on the Observer pattern where the subject, in this case RunModeOptions can have many observers which for us means the RunModeDialog class.

We extend the WindowAdapter class so we can action a callback method when a WindowEvent has been actioned by the user. In our case an event handler to process situations where the user has closed the window rather than clicking one of the buttons. See the windowClosing() method for details.

Create the RunModeDialog class in the client package and cut and paste the following code into it.


package client;

import java.awt.Frame;
import java.awt.event.*;
import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Observable;
import java.util.Observer;
import java.util.logging.*;
import javax.swing.*;

/**
 * Dialog box to accept and validate run mode options for the Manufacturer application. 
 * This class provides a standard dialog box which allows the user to select the location
 * of the file (which may be a physical file in NON_NETWORK_CLIENT mode, or the address
 * (and, optionally, the port) of the server for NETWORK_CLIENT mode.
 */
public class RunModeDialog extends WindowAdapter implements ActionListener, Observer {
    /**
     * The Logger instance. All log messages from this class are routed through
     * this member. The Logger namespace is J8CaseStudy.
     */
    private static Logger log = Logger.getLogger("J8CaseStudy");

    /*
     * The strings for the title and buttons in the dialog box. 
     */
    private static final String TITLE = "Please enter Manufacturer file location";
    private static final String CONNECT = "Connect";
    private static final String EXIT = "Exit";

    /*
     * Some port range values to help determine what sort of port was specified.
     */
    private static final int LOWEST_PORT = 0;
    private static final int HIGHEST_PORT = 65535;
    private static final int SYSTEM_PORT_BOUNDARY = 1024;

    /*
     * Global components for our dialog box that we can disable / enable 
     * as a user enters valid information.
     */
    private JOptionPane options;
    private JDialog dialog;
    private JButton connectButton = new JButton(CONNECT);
    private JButton exitButton = new JButton(EXIT);

    /*
     * Common panel used by both the client and the server for
     * specifying file location.
     */
    private RunModeOptions runModeOptions;

    /*
     * Flags to validate application startup.
     */
    private boolean validFilePathName = false;
    private boolean validPort = false;

    /*
     * Details specified in the RunModeOptions pane detailing where the file is.
     */
    private String filePathName;
    private String port = null;

    /**
     * Creates a dialog where the user can specify the location of the
     * file, and IP address and port number(networked client), or 
     * search and select the file on a local drive if this is a 
     * non-networked client.
     *
     * @param parent Defines the Component that is used as the parent of this
     * dialog box. 
     * @param userMode Specifies the type of connection (non-networked or networked)
     * @see JOptionPane
     */
    public RunModeDialog(Frame parent, RunMode userMode) {
        log.entering("RunModeDialog", "RunModeDialog");
        runModeOptions = (new RunModeOptions(userMode));
        runModeOptions.getObservable().addObserver(this);
        // Load saved configuration
        SavedRunModeOptions savedRunModeOptions = 
        		SavedRunModeOptions.getSavedRunModeInstance();
        // Port not required for stand alone mode
        if (userMode == RunMode.NON_NETWORK_CLIENT) {
            validPort = true;
            filePathName = savedRunModeOptions.getParameter(SavedRunModeOptions.FILE_LOCATION);
        } else {
            /*
             * There is always at least a default port number, so we don't have
             * to validate this.
             */
            port = savedRunModeOptions.getParameter(SavedRunModeOptions.SERVER_PORT);
            runModeOptions.setPortNumberText(port);
            validPort = true;
            filePathName = savedRunModeOptions.getParameter(SavedRunModeOptions.SERVER_ADDRESS);
        }
        /*
         * There may not be a default file location, so we had better
         * validate before using the returned value.
         */
        if (filePathName != null) {
            runModeOptions.setLocationFieldText(filePathName);
            validFilePathName = true;
        }
        options = new JOptionPane(runModeOptions, JOptionPane.QUESTION_MESSAGE,
                                  JOptionPane.OK_CANCEL_OPTION);
        connectButton.setActionCommand(CONNECT);
        connectButton.addActionListener(this);

        boolean allValid = validFilePathName && validPort;
        connectButton.setEnabled(allValid);

        exitButton.setActionCommand(EXIT);
        exitButton.addActionListener(this);

        options.setOptions(new Object[] {connectButton, exitButton});

        dialog = options.createDialog(parent, TITLE);
        dialog.setDefaultCloseOperation(JDialog.DO_NOTHING_ON_CLOSE);
        dialog.addWindowListener(this);
        dialog.setVisible(true);
        log.exiting("RunModeDialog", "RunModeDialog");
    }

    /*
     *  Callback methods.
     */
    /**
     * Callback event handler to process situations where the user has closed
     * the window rather than clicking one of the buttons.
     */
    public void windowClosing(WindowEvent we) {
        log.entering("RunModeDialog", "windowClosing");
        log.exiting("RunModeDialog", "windowClosing");
        processCommand(EXIT);
    }

    /**
     * Callback event handler to process clicks on any of the buttons.
     */
    public void actionPerformed(ActionEvent ae) {
        log.entering("RunModeDialog", "actionPerformed");
        processCommand(ae.getActionCommand());
        log.exiting("RunModeDialog", "actionPerformed");
    }

    /**
     * Common event handling code - can handle desirable actions (such as
     * buttons being clicked) and undesirable actions (the window being
     * closed) all in a common location.
     *
     * @param command a String representing the action that occurred.
     */
    private void processCommand(String command) {
        log.entering("RunModeDialog", "processCommand");
        dialog.setVisible(false);
        
        if (CONNECT.equals(command)) {
            options.setValue(JOptionPane.OK_OPTION);
        } else {
            options.setValue(JOptionPane.CANCEL_OPTION);
        }
        log.exiting("RunModeDialog", "processCommand");
    }

    /**
     * Let the caller of this dialog know whether the user connected or
     * cancelled.
     *
     * @return true if the user cancelled or closed the window.
     */
    public boolean userCancelled() {
        if (options.getValue() instanceof Integer) {
            int status = (Integer) options.getValue();
            return status != JOptionPane.OK_OPTION;
        } else {
            return false;
        }
    }

    /**
     * Callback method to process modifications in the common RunModeOptions
     * panel. RunModeOptions sends updates to registered Observers whenever
     * anything changes. When something changes we validate here and if
     * changes are valid we enable the "Connect" button of the dialog box.
     */
    public void update(Observable o, Object arg) {
        log.entering("RunModeDialog", "update");
        /*
         * we are going to ignore the Observable object, since we are only
         * observing one object. All we are interested in is the argument.
         */  
        if (!(arg instanceof RunModeOptionsUpdate)) {
            log.log(Level.WARNING, "Run Mode Dialog received update type: " 
            		+ arg, new IllegalArgumentException());
            return;
        }
        RunModeOptionsUpdate runModeOptionsUpdate = (RunModeOptionsUpdate) arg;
        // Load saved configuration
        SavedRunModeOptions savedRunModeOptions = 
        		SavedRunModeOptions.getSavedRunModeInstance();

        // Process changes
        switch (runModeOptionsUpdate.getUpdateType()) {
            case FILE_LOCATION_MODIFIED:
            	filePathName = (String) runModeOptionsUpdate.getPayload();
                if (runModeOptions.getApplicationMode() 
                        == RunMode.NON_NETWORK_CLIENT) {
                    File f = new File(filePathName);
                    if (f.exists() && f.canRead() && f.canWrite()) {
                        validFilePathName = true;
                        log.info("File chosen " + filePathName);
                        savedRunModeOptions.setParameter
                        		(SavedRunModeOptions.FILE_LOCATION, 
                        				filePathName);
                    } else {
                        log.warning("Invalid file " + filePathName);
                    }
                } else {
                    try {
                        if (filePathName.matches("\\d+\\.\\d+\\.\\d+\\.\\d+")) {
                            String[] quads = filePathName.split("\\.");
                            byte[] address = new byte[quads.length];
                            for (int i = 0; i < quads.length; i++) {
                                address[i] = new Integer(quads[i]).byteValue();
                            }
                            InetAddress.getByAddress(address);
                        } else {
                            InetAddress.getAllByName(filePathName);
                        }
                        log.info("Server specified " + filePathName);
                        validFilePathName = true;
                        savedRunModeOptions.setParameter(SavedRunModeOptions.SERVER_ADDRESS,
                        		filePathName);
                    } catch (UnknownHostException uhe) {
                        log.warning("Unknown host: " + filePathName);
                        validFilePathName = false;
                    }
                }
                break;
            case PORT_MODIFIED:
                port = (String) runModeOptionsUpdate.getPayload();
                int p = Integer.parseInt(port);
                // Log an info message for port numbers
                if (p >= LOWEST_PORT && p < HIGHEST_PORT) {
                    if (p < SYSTEM_PORT_BOUNDARY) {
                        log.info("User chose System port " + port);
                    } else {
                        log.info("User chose dynamic port " + port);
                    }
                    validPort = true;
                    savedRunModeOptions.setParameter(SavedRunModeOptions.SERVER_PORT, port);
                } else {
                    validPort = false;
                }
                break;
            default:
                log.warning("Unknown update: " + runModeOptionsUpdate);
                break;
        }
        boolean allValid = validFilePathName && validPort;
        connectButton.setEnabled(allValid);
        log.exiting("RunModeDialog", "update");
    }

    /*
     *  Getter methods.
     */
    /**
     * Returns the location of the file, which may be either the path to
     * the local file, or the address of the network server hosting the
     * file.
     *
     * @return The location of the file.
     */
    public String getLocation() {
        log.entering("RunModeDialog", "getLocation");
        log.exiting("RunModeDialog", "getLocation", this.filePathName);
        return this.filePathName;
    }

    /**
     * Returns the port number the network server should be listening on for
     * client connections.
     *
     * @return The port number for connecting to the network server.
     */
    public String getPort() {
        log.entering("RunModeDialog", "getPort");
        log.exiting("RunModeDialog", "getPort", this.port);
        return this.port;
    }
}

The following screenshot shows the client package structure after adding the RunModeDialog class.

create RunModeDialog
Screenshot 1. The client package after adding the ServicesImpl class.

Lesson 15 Complete

In this lesson we coded the View elements of the MVC pattern that relate to the run mode options.

Related Java Tutorials

Fundamentals - Primitive Variables
Fundamentals - if Construct
Fundamentals - switch Construct
Objects & Classes - Arrays
Objects & Classes - Class Structure and Syntax
Objects & Classes - Reference Variables
Objects & Classes - Methods
Objects & Classes - Instance Variables & Scope
Objects & Classes - Constructors
Objects & Classes - Static Members
Objects & Classes - Enumerations
OO Concepts - Encapsulation
OO Concepts - Inheritance Concepts - Using the super keyword
Exceptions - Handling Exceptions
API Contents - Inheritance - Using the package keyword
API Contents - Inheritance - Using the import keyword
API Contents - Java I/O Overview - The java.io.File Class
Swing - Containers - The javax.swing.JFrame Class
Swing - Event Handling - Using the ActionListener Interface
Swing - Dialogs
Swing - Components

What's Next?

In the next lesson we code the View elements of the MVC pattern that relate to the server.