View Part 1 - Run ModesJ8 Home « View Part 1 - Run Modes
In the final lesson of the View Part 1 section of the case study we code classes to create a panel for entry of run mode options and and persist these for later reuse to avoid having to reenter them each time the application is used.
Creating The SavedRunModeOptions
Class Top
The SavedRunModeOptions
class provides read/write access to the user's saved run mode parameters on disk, so that next time they connect, we can offer the same run mode parameters as a default.
Create the SavedRunModeOptions
class in the client
package and cut and paste the following code into it.
package client;
import java.io.*;
import java.util.*;
import java.util.logging.Logger;
/**
* Provides read/write access to the user's saved run mode parameters on disk, so
* that next time they connect, we can offer the same run mode parameters as a default.
*
*/
public class SavedRunModeOptions {
/**
* 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"); // Log output
/**
* Key in Properties indicating that the value will be the manufacturer
* file location.
*/
public static final String FILE_LOCATION = "FileLocation";
/**
* Key in Properties indicating that the value will be the RMI Registry
* server address.
*/
public static final String SERVER_ADDRESS = "ServerAddress";
/**
* Key in Properties indicating that the value will be the port the RMI
* registry listens on.
*/
public static final String SERVER_PORT = "ServerPort";
/**
* The location where our SavedRunModeOptions file will be saved.
*/
private static final String BASE_DIRECTORY = System.getProperty("user.dir");
/**
* the name of our properties file.
*/
private static final String OPTIONS_FILENAME = "manufacturer.properties";
/**
* The file containing our saved run mode options.
*/
private static final File savedRunModeOptionsFile = new File(BASE_DIRECTORY, OPTIONS_FILENAME);
/**
* Only create SavedRunModeOptions Singleton first time through using double-checked
* locking to ensure private constructor is only synchronised once.
*/
private volatile static SavedRunModeOptions uniqueSavedRunModeInstance;
/**
* The Properties for this application.
*/
private Properties parameters = null;
/**
* Creates a new instance of SavedRunModeOptions. There should only ever be
* one instance of this class (a Singleton), so we have made it private.
*/
private SavedRunModeOptions() {
log.entering("SavedRunModeOptions", "SavedRunModeOptions");
parameters = loadParametersFromFile();
if (parameters == null) {
parameters = new Properties();
parameters.setProperty(SERVER_ADDRESS, "localhost");
parameters.setProperty(SERVER_PORT, "" + java.rmi.registry.Registry.REGISTRY_PORT);
}
log.exiting("SavedRunModeOptions", "SavedRunModeOptions");
}
/**
* Clients of the SavedRunModeOptions class have to call this method to get the unique
* instance (singleton) for this class as the constructor is private.
*
* @return The singleton for SavedRunModeOptions class.
*/
public static SavedRunModeOptions getSavedRunModeInstance() {
if (uniqueSavedRunModeInstance == null) {
synchronized (SavedRunModeOptions.class) {
if (uniqueSavedRunModeInstance == null) {
uniqueSavedRunModeInstance = new SavedRunModeOptions();
}
}
}
return uniqueSavedRunModeInstance;
}
/**
* Returns the value of the named parameter.
*
* @param parameterName The name of the run mode parameter.
* @return The value of the named run mode parameter.
*/
public String getParameter(String parameterName) {
log.entering("SavedRunModeOptions", "getParameter", parameterName);
log.exiting("SavedRunModeOptions", "getParameter", parameters.getProperty(parameterName));
return parameters.getProperty(parameterName);
}
/**
* Updates the saved run mode parameters with the new values.
*
* @param parameterName The name of the run mode parameter.
* @param parameterValue The value run mode parameter to be stored.
*/
public void setParameter(String parameterName, String parameterValue) {
log.entering("SavedRunModeOptions", "setParameter",
new Object[] {parameterName, parameterValue});
parameters.setProperty(parameterName, parameterValue);
saveParametersToFile();
log.exiting("SavedRunModeOptions", "setParameter");
}
/**
* Saves the parameters to a file so that they can be used again next time
* the application starts.
*/
private void saveParametersToFile() {
log.entering("SavedRunModeOptions", "saveParametersToFile");
try {
synchronized (savedRunModeOptionsFile) {
if (savedRunModeOptionsFile.exists()) {
savedRunModeOptionsFile.delete();
}
savedRunModeOptionsFile.createNewFile();
FileOutputStream fos = new FileOutputStream(savedRunModeOptionsFile);
parameters.store(fos, "Manufacturer Application run mode options");
fos.close();
}
} catch (IOException e) {
ManufacturerApplicationStartup.handleException("Unable to save run mode parameters"
+ " to file. They wont be remembered next time you start.");
}
log.exiting("SavedRunModeOptions", "saveParametersToFile");
}
/**
* Attempts to load the saved run mode parameter from the file so that the user does
* not have to reenter all the information.
*
* @return Properties loaded from file or null.
*/
private Properties loadParametersFromFile() {
log.entering("SavedRunModeOptions", "loadParametersFromFile");
Properties loadedProperties = null;
if (savedRunModeOptionsFile.exists() && savedRunModeOptionsFile.canRead()) {
synchronized (savedRunModeOptionsFile) {
try {
FileInputStream fis = new FileInputStream(savedRunModeOptionsFile);
loadedProperties = new Properties();
loadedProperties.load(fis);
fis.close();
} catch (IOException e) {
ManufacturerApplicationStartup.handleException("Unable to load run mode "
+ "parameters. Default values will be used.\n" + e);
}
}
}
log.exiting("SavedRunModeOptions", "loadParametersFromFile", loadedProperties);
return loadedProperties;
}
}
Creating The RunModeOptions
Class Top
The RunModeOptions
class creates a common panel used by both the client and server applications to specify the run mode options. The panel changes slightly dependent on run mode entered
on application startup but has a "common feel" for all three run modes. A point of interest here is that we create an inner class that extends the java.util.Observable
class; This class represents
an observable object, or "data" in the model-view paradigm. An observable object can have one or more observers, which are objects that implements the Observer
interface. What this means
for the case study is that the RunModeDialog
class which we will code in Validate Run Mode Options will be notified whenever changes are
made within the run mode options panel, so they can be validated within that dialog.
Create the RunModeOptions
class in the client
package and cut and paste the following code into it.
package client;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.util.logging.Logger;
import java.util.Observable;
import javax.swing.*;
/**
* A common panel used by both the client and server applications to specify the
* run mode options. The panel changes slightly dependent on run mode entered
* on application startup but has a "common feel" for all three run modes.
*
* @see RunMode
*
*/
public class RunModeOptions extends JPanel {
/**
* 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"); // Log output
/**
* A version number for the RunModeOptions class so that serialisation can
* occur without worrying about the underlying class changing between
* serialisation and deserialisation.
*/
private static final long serialVersionUID = 2498052502L;
// Constants.
private static final String FILE_LOCATION_LABEL = "Manufacturer file location: ";
private static final String SERVER_PORT_LABEL = "Server port: ";
private static final String FILE_LOCATION_TOOL_TIP
= "The location of the Manufacturer file on the hard drive";
private static final String FILE_IP_LOCATION_TOOL_TIP
= "The server where the Manufacturer file is located (IP address)";
private static final String SERVER_PORT_TOOL_TIP
= "The port number the Server uses to listens for requests";
private static final String FILE_EXTENSION = "txt";
private static final String FILE_CHOOSER_DESCRIPTION
= "Database files (*." + FILE_EXTENSION + ")";
/**
* An Observable class so interested users of this class can receive
* automatic updates whenever run mode options change.
*/
private RunModeObservable observerRunModeOptions = new RunModeObservable();
// User modifiable fields and all buttons are defined here.
private JTextField locationField = new JTextField(40);
private JButton browseButton = new JButton("...");
private JTextField portNumber = new PositiveIntVerify(5);
private String location = null;
private String port = null;
private RunMode runMode = RunMode.NON_NETWORK_CLIENT;
/**
* Creates a new instance of RunModeOptions - the panel for configuring
* database connectivity.
*
* @param runMode One of NON_NETWORK_CLIENT
,
* NETWORK_CLIENT
, or SERVER
.
*
* @see RunMode
*/
public RunModeOptions(RunMode runMode) {
super();
log.entering("RunModeOptions", "RunModeOptions");
this.runMode = runMode;
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints constraints = new GridBagConstraints();
this.setLayout(gridbag);
/*
* Standard options
* Ensure there is always a gap between components
*/
constraints.insets = new Insets(3, 3, 3, 3);
// Build the Manufacturer file location row
JLabel dbLocationLabel = new JLabel(FILE_LOCATION_LABEL);
gridbag.setConstraints(dbLocationLabel, constraints);
this.add(dbLocationLabel);
if (runMode == RunMode.NETWORK_CLIENT) {
locationField.setToolTipText(FILE_IP_LOCATION_TOOL_TIP);
constraints.gridwidth = GridBagConstraints.REMAINDER; // End row
} else {
locationField.setToolTipText(FILE_LOCATION_TOOL_TIP); // Next-to-last in row
constraints.gridwidth = GridBagConstraints.RELATIVE;
}
locationField.addFocusListener(new ActionHandler());
locationField.setName(FILE_LOCATION_LABEL);
gridbag.setConstraints(locationField, constraints);
this.add(locationField);
/*
* Add a browse button to allow navigation to the Manufacturer file
* in Server and non-network client modes.
*/
if ((runMode == RunMode.SERVER)
|| (runMode == RunMode.NON_NETWORK_CLIENT)) {
browseButton.addActionListener(new BrowseForFile());
constraints.gridwidth = GridBagConstraints.REMAINDER; // End row
gridbag.setConstraints(browseButton, constraints);
this.add(browseButton);
}
if ((runMode == RunMode.SERVER)
|| (runMode == RunMode.NETWORK_CLIENT)) {
// Build the Server port row if applicable
constraints.weightx = 0.0;
JLabel serverPortLabel = new JLabel(SERVER_PORT_LABEL);
constraints.gridwidth = 1;
constraints.anchor = GridBagConstraints.EAST;
gridbag.setConstraints(serverPortLabel, constraints);
this.add(serverPortLabel);
portNumber.addFocusListener(new ActionHandler());
portNumber.setToolTipText(SERVER_PORT_TOOL_TIP);
portNumber.setName(SERVER_PORT_LABEL);
constraints.gridwidth = GridBagConstraints.REMAINDER; // End row
constraints.anchor = GridBagConstraints.WEST;
gridbag.setConstraints(portNumber, constraints);
this.add(portNumber);
}
log.exiting("RunModeOptions", "RunModeOptions");
}
/**
* Utility method to inform our observers of any changes the user makes to
* the parameters on screen.
*
* @param updateType Enum specify which field has changed
* @param payLoad the new data the user just entered.
*/
private void updateObservers(RunModeOptionsUpdate.Update updateType, Object payLoad) {
log.entering("RunModeOptions", "updateObservers");
RunModeOptionsUpdate update = new RunModeOptionsUpdate(updateType, payLoad);
observerRunModeOptions.setChanged();
observerRunModeOptions.notifyObservers(update);
log.exiting("RunModeOptions", "updateObservers");
}
/**
* A utility class to handle user interactions with the panel. These are
* not processed by the user of this panel, rather they are processed
* internally, and an update is then sent to any observers.
*/
private class ActionHandler implements FocusListener {
/** {@inheritDoc} */
public void focusGained(FocusEvent e) {
// Ignored as we don't do anything particular when users enter a field
}
/** {@inheritDoc} */
public void focusLost(FocusEvent e) {
if (FILE_LOCATION_LABEL.equals(e.getComponent().getName())
&& (!locationField.getText().equals(location))) {
location = locationField.getText();
updateObservers(RunModeOptionsUpdate.Update.FILE_LOCATION_MODIFIED,
location.trim());
}
if (SERVER_PORT_LABEL.equals(e.getComponent().getName())
&& (!portNumber.getText().equals(port))) {
port = portNumber.getText();
updateObservers(RunModeOptionsUpdate.Update.PORT_MODIFIED, port.trim());
}
}
}
/**
* A utility class that provides the user with the ability to browse for
* the Manufacturer file rather than forcing them to remember (and type in)
* a fully qualified Manufacturer file location.
*/
private class BrowseForFile implements ActionListener {
/** {@inheritDoc} */
public void actionPerformed(ActionEvent ae) {
JFileChooser chooser = new JFileChooser(System.getProperty("user.dir"));
chooser.addChoosableFileFilter(
new javax.swing.filechooser.FileFilter() {
/**
* display files ending in ".txt" or any other object
* (directory or other selectable device).
*/
public boolean accept(File f) {
if (f.isFile()) {
return f.getName().endsWith(FILE_EXTENSION);
} else {
return true;
}
}
/**
* provide a description for the types of files we are
* allowing to be selected.
*/
public String getDescription() {
return FILE_CHOOSER_DESCRIPTION;
}
}
);
// if the user selected a file, update the file name on screen
if (JFileChooser.APPROVE_OPTION == chooser.showOpenDialog(null)) {
locationField.setText(chooser.getSelectedFile().toString());
location = locationField.getText();
updateObservers(RunModeOptionsUpdate.Update.FILE_LOCATION_MODIFIED,
location.trim());
}
}
}
/**
* Returns the run mode the Manufacturer application will be running in.
*
* @return The run mode the Manufacturer application will be running in.
* @see RunMode
*/
public RunMode getApplicationMode() {
log.entering("RunModeOptions", "getApplicationMode");
log.exiting("RunModeOptions", "getApplicationMode", this.runMode);
return this.runMode;
}
/**
* Returns the contents of the Manufacturer location field.
*
* @return the contents of the Manufacturer location field.
*/
public String getLocationFieldText() {
log.entering("RunModeOptions", "getLocationFieldText");
log.exiting("RunModeOptions", "getLocationFieldText", locationField.getText());
return locationField.getText();
}
/**
* Sets the contents of the Manufacturer location field.
*
* @param locationField the contents of the database location field.
*/
public void setLocationFieldText(String locationField) {
log.entering("RunModeOptions", "setLocationFieldText", locationField);
this.locationField.setText(locationField);
log.exiting("RunModeOptions", "setLocationFieldText");
}
/**
* Configures whether the location field is enabled or not.
*
* @param enabled true if the location field is enabled.
*/
public void setLocationFieldEnabled(boolean enabled) {
log.entering("RunModeOptions", "setLocationFieldEnabled", enabled);
this.locationField.setEnabled(enabled);
log.exiting("RunModeOptions", "setLocationFieldEnabled");
}
/**
* Configures whether the browse button is enabled or not.
*
* @param enabled true if the browse button is enabled.
*/
public void setBrowseButtonEnabled(boolean enabled) {
log.entering("RunModeOptions", "setBrowseButtonEnabled", enabled);
this.browseButton.setEnabled(enabled);
log.exiting("RunModeOptions", "setBrowseButtonEnabled");
}
/**
* Returns the contents of the port number text field.
*
* @return the contents of the port number text field.
*/
public String getPortNumberText() {
log.entering("RunModeOptions", "getPortNumberText");
log.exiting("RunModeOptions", "getPortNumberText", portNumber.getText());
return portNumber.getText();
}
/**
* Sets the contents of the port number text field.
*
* @param portNumber the contents of the port number text field.
*/
public void setPortNumberText(String portNumber) {
log.entering("RunModeOptions", "setPortNumberText", portNumber);
this.portNumber.setText(portNumber);
log.exiting("RunModeOptions", "setPortNumberText");
}
/**
* Configures whether the port number field is enabled or not.
*
* @param enabled true if the port number field is enabled.
*/
public void setPortNumberEnabled(boolean enabled) {
log.entering("RunModeOptions", "setPortNumberEnabled", enabled);
this.portNumber.setEnabled(enabled);
log.exiting("RunModeOptions", "setPortNumberEnabled");
}
/**
* Returns an instance of the Observable
class. Observers can
* register themselves with this class in order to receive updates as
* things change within this panel.
*
* @return an instance of the Observable
class.
*/
public Observable getObservable() {
log.entering("RunModeOptions", "getObservable");
log.exiting("RunModeOptions", "getObservable", observerRunModeOptions);
return observerRunModeOptions;
}
/**
* Our Observable class - a class that Observers can register themselves
* with in order to receive updates as things change within this panel.
*/
private class RunModeObservable extends Observable {
/** {@inheritDoc} */
public void setChanged() {
super.setChanged();
}
}
}
After adding the SavedRunModeOptions
and RunModeOptions
classes your client
package should look similar to that in the screenshot below.
Lesson 10 Complete
In this lesson we coded the View elements of the MVC pattern that are concerned with run mode options.
Related Java Tutorials
Fundamentals - Primitive Variables
Fundamentals - Conditional Statements
Fundamentals - Loop Statements
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
Swing - RMI - Serialization
Exceptions - Handling Exceptions
API Contents - Inheritance - Using the package
keyword
API Contents - Inheritance - Using the import
keyword
What's Next?
In the next section we set up the first part of the Controller elements of the MVC pattern that can be derived from the project proposal.