Independent GUI ClassesHomepage  « Case Study « Independent GUI Classes

We will start coding the view part of the MVC pattern, starting with the classes that are independent of other classes within the application.

These classes have no reliance on other classes and so will compile cleanly and make compilation of the rest of the view part of the Manufacturer application easier.

Compiling The ExitManufacturerFileCleanly Classgo to top of page Top

The ExitManufacturerFileCleanly class gets registered with the JVM in the ManufacturerServerStartupWindow class (yet to be written) to run when the Manufacturer application is shutting down by calling Runtime.addShutdownHook with an instance of this class as a parameter. The run method will be executed when the Manufacturer application is shut down by the user, ensuring that we are not in the process of writing to the Manufacturer file, which might result in a corrupted file.

Cut and paste the following code into your text editor and save it in the   c:\_Case_Study\src\client directory.


package client;

import java.util.logging.Level;
import java.util.logging.Logger;

import model.StockImpl;
import model.ManufacturerFileAccessException;

/**
 * This class is registered with the JVM in the ManufacturerServerStartupWindow 
 * class to run when the application is shutting down by calling 
 * Runtime.addShutdownHook with an instance of this class as a
 * parameter.
 *
 * The run method will be executed when the  application is shut 
 * down by the user, ensuring that we are not in the process of writing to the 
 * Manufacturer file, which might result in a corrupted file.
 * 
 * @see java.lang.Runtime#addShutdownHook
 *
 * @author Kevin 
 * @version 1.0
 */
public class ExitManufacturerFileCleanly extends Thread {

    /**
     * The Logger instance through which all log messages from this class are
     * routed. Logger namespace is s2cCaseStudy.
     */
    private Logger log = Logger.getLogger("s2cCaseStudy"); // Log output

    /**
     * The location of our Manufacturer file,  so that we can connect to
     * it correctly prior to locking it from updates.
     */
    private String manufacturerPathName = null;

    /**
     * Create an instance of this class on server startup so that it can be run 
     * later. 
     *
     * @param manufacturerPathName The location of the Manufacturer file
     *  on the disk.
     */
    public ExitManufacturerFileCleanly(String manufacturerPathName) {
        this.manufacturerPathName = manufacturerPathName;
    }
  
    /**
     * This method is executed by the JVM when the application is shutdown,
     * ensuring the Manufacturer file is in a clean state for shutdown by
     * gaining an exclusive lock on the locking map.
     */
    public void run() {
        log.info("Ensuring a clean Manufacturer file shutdown");
        try {
            StockImpl stock = model.StockImpl.getStockImplInstance(manufacturerPathName);
            stock.lockLockingMap(true);
        } catch (ManufacturerFileAccessException e) {
            log.log(Level.SEVERE, "Failed to lock locking map before exiting", e);
        }
    }
}

Compiling Our Source File With the -cp and -d Options

Open your command line editor:

Change to directory  cd c:\_Case_Study\src\client

Compile ExitManufacturerFileCleanly.java using the java compiler with the -cp and -d options
  javac -cp ..\..\classes -d ..\..\classes ExitManufacturerFileCleanly.java

Compiling The ManufacturerTableModel Classgo to top of page Top

The ManufacturerTableModel class is a custom table model used by the ManufacturerWindow instance, which we will write in the View Part 2 section. This custom table model allows us to get and set table entries along with some other useful utilities that are documented within the code. There are a lot of methods to implement within the TableModel interface so we are extending the AbstractTableModel interface which allows us to only implement those methods we need.


package client;

import java.util.ArrayList;
import java.util.logging.*;
import javax.swing.table.AbstractTableModel;
import model.Manufacturer;

/**
 * The custom table model used by the ManufacturerWindow instance.
 * 
 * @author Kevin 
 * @version 1.0
 * @see model.StockImpl
 *
 */
public class ManufacturerTableModel extends AbstractTableModel {
    /**
     * The Logger instance through which all log messages from this class are routed.
     * Logger namespace is s2cCaseStudy.
     */
    private static Logger log = Logger.getLogger("s2cCaseStudy"); // Log output

    /**
     * A version number for the ManufacturerTableModel class so that serialisation 
     * can occur without worrying about the underlying class changing 
     * between serialisation and deserialisation.
     */
    private static final long serialVersionUID = 871964L;

    /**
     * An array of String objects representing the table headers.
     */
    private String [] headerNames = {"Name", "Location", "Product", "Product Price", 
    		                         "Stock Level", "Stock Ordered"};

    /**
     * Holds all Manufacturer instances displayed in the main table.
     */
    private ArrayList<String[]> manufacturerRecords = new ArrayList<String[]>(2);

    /**
     * Returns the column count of the table.
     *
     * @return An integer indicating the number or columns in the table.
     */
    public int getColumnCount() {
        log.entering("ManufacturerTableModel", "getColumnCount");
        log.exiting("ManufacturerTableModel", "getColumnCount", this.headerNames.length);
        return this.headerNames.length;
    }

    /**
     * Returns the number of rows in the table.
     *
     * @return An integer indicating the number of rows in the table.
     */
    public int getRowCount() {
        log.entering("ManufacturerTableModel", "getRowCount");
        log.exiting("ManufacturerTableModel", "getRowCount", this.manufacturerRecords.size());
        return this.manufacturerRecords.size();
    }

    /**
     * Gets a value from a specified index in the table.
     *
     * @param row An integer representing the row index.
     * @param column An integer representing the column index.
     * @return The object located at the specified row and column.
     */
    public Object getValueAt(int row, int column) {
        log.entering("ManufacturerTableModel", "getValueAt", new Object[]{row, column});
        String[] rowValues = this.manufacturerRecords.get(row);
        log.exiting("ManufacturerTableModel", "getValueAt", rowValues[column]);
        return rowValues[column];
    }

    /**
     * Sets the cell value at a specified index.
     *
     * @param obj The object that is placed in the table cell.
     * @param row The row index.
     * @param column The column index.
     */
    public void setValueAt(Object obj, int row, int column) {
        log.entering("ManufacturerTableModel", "setValueAt", new Object[]{row, column});
        Object[] rowValues = this.manufacturerRecords.get(row);
        rowValues[column] = obj;
        log.exiting("ManufacturerTableModel", "setValueAt");
    }

    /**
     * Returns the name of a column at a given column index.
     *
     * @param column The specified column index.
     * @return A String containing the column name.
     */
    public String getColumnName(int column) {
        return headerNames[column];
    }
	
    /**
     * Adds a row of Manufacturer data to the table.
     *
     * @param name The Manufacturers name.
     * @param location The location where the Manufacturer is based.
     * @param product Name of the product.
     * @param price Price of a product. 
     * @param stockLevel Product stock level.
     * @param stockOrdered Stock on order.
     */
    private void addManufacturerRecord(String name, String location,
            String product, String price, String stockLevel, String stockOrdered) {
        log.entering("ManufacturerTableModel", "addManufacturerRecord", 
                new Object[]{name, location, product, price, stockLevel, stockOrdered});
        String[] manufacturerData = {name, location, product, price, stockLevel, stockOrdered};
        this.manufacturerRecords.add(manufacturerData);
        log.exiting("ManufacturerTableModel", "addManufacturerRecord");
    }

    /**
     * Splits the Manufacturer into its components.
     *
     * @param manufacturer A Manufacturer object.
     */
    public void addManufacturerRecord(Manufacturer manufacturer) {
        log.entering("ManufacturerTableModel", "addManufacturerRecord");
        addManufacturerRecord(manufacturer.getName(), manufacturer.getLocation(), 
                manufacturer.getProduct(), manufacturer.getPrice(),
                manufacturer.getStockLevel(), manufacturer.getStockOrdered());
        log.exiting("ManufacturerTableModel", "addManufacturerRecord");
    }
}

Compiling Our Source File With the -cp and -d Options

Open your command line editor:

Change to directory  cd c:\_Case_Study\src\client

Compile ManufacturerTableModel.java using the java compiler with the -cp and -d options
  javac -cp ..\..\classes -d ..\..\classes ManufacturerTableModel.java

Compiling The PositiveIntVerify Classgo to top of page Top

The PositiveIntVerify class is an extension of JTextField which only allows positive integer numbers to be entered. This reduces the validation required on the field after entry, and gives a nicer entry field to the end user, as they can only enter digits.

Cut and paste the following code into your text editor and save it in the   c:\_Case_Study\src\client directory.


package client;

import java.util.logging.Logger;
import javax.swing.JTextField;
import javax.swing.text.*;

/**
 * Extension of JTextField which only allows positive int numbers to be
 * entered.
 *
 * This reduces the validation required on the field after entry, and gives a
 * nicer entry field to the end user - as they can only enter digits.
 */
public class PositiveIntVerify extends JTextField {
    /**
     * The Logger instance. All log messages from this class are routed through
     * this member. The Logger namespace is s2cCaseStudy.
     */
    private Logger log = Logger.getLogger("s2cCaseStudy");

    /**
     * A version number for the PositiveIntVerify class so that serialisation 
     * can occur without worrying about the underlying class changing between
     * serialisation and deserialisation.
     */
    private static final long serialVersionUID = 871964L;

    /**
     * Constructs a new empty PositiveIntVerify with zero columns.
     * The default document will be a NumericDocument which only allows
     * positive integer numbers to be entered.
     */
    public PositiveIntVerify() {
        super();
        log.entering("PositiveIntVerify", "PositiveIntVerify");
        log.exiting("PositiveIntVerify", "PositiveIntVerify");
    }

    /**
     * Constructs a new empty PositiveIntVerify with the specified number of
     * columns. The default document will be a NumericDocument which only allows
     * positive integer numbers to be entered.
     *
     * @param columns Number of columns to use to calculate preferred width. 
     * if columns is set to zero, the preferred width will be whatever
     * naturally results from the component implementation
     */
    public PositiveIntVerify(int columns) {
        super(columns);
        log.entering("PositiveIntVerify", "PositiveIntVerify");
        log.exiting("PositiveIntVerify", "PositiveIntVerify");
    }

    /**
     * Creates default implementation of the model to be used at construction. 
     *
     * @return NumericDocument a document which only allows positive integers
     */
    protected Document createDefaultModel() {
        return new NumericDocument();
    }

    /**
     * A document that only allows positive integer numbers to be entered.
     */
    class NumericDocument extends PlainDocument {
    	/**
    	 * A version number for the NumericDocument class so that serialisation 
    	 * can occur without worrying about the underlying class changing 
    	 * between serialisation and deserialisation.
    	 */
    	private static final long serialVersionUID = 871964L;

        /**
         * Inserts some content into the document. Inserting content causes a
         * write lock to be held while the actual changes are taking place,
         * followed by notification to the observers on the thread that grabbed
         * the write lock.
         *
         * @param offset The starting offset >= 0.
         * @param str The string to insert; does nothing with null/empty strings.
         * @param a The attributes for the inserted content.
         * 
         * @throws BadLocationException The given insert position is not a
         * valid position within the document.
         */
        public void insertString(int offset, String str, AttributeSet a) 
        		throws BadLocationException {
            if (str == null) {
                return;
            }
            char[] input = str.toCharArray();
            int result = 0;
            boolean valid = false;

            for (int i = 0; i < input.length; i++) {
                if (Character.isDigit(input[i])) {
                    valid = true;
                    result = result * 10 + Character.digit(input[i], 10);
                }
            }
            if (valid) {
                super.insertString(offset, new String("" + result), a);
            }
        }
    }
}

Compiling Our Source File With the -cp and -d Options

Open your command line editor:

Change to directory  cd c:\_Case_Study\src\client

Compile PositiveIntVerify.java using the java compiler with the -cp and -d options
  javac -cp ..\..\classes -d ..\..\classes PositiveIntVerify.java

Compiling The RunMode Classgo to top of page Top

The RunMode class is an enumeration specifies the mode the Manufacturer application is running in..

Cut and paste the following code into your text editor and save it in the   c:\_Case_Study\src\client directory.


package client;

/** 
 * Specifies the mode the Manufacturer application is running in.
 * 
 * NON_NETWORK_CLIENT - Application running in non-networked client mode.
 * NETWORK_CLIENT     - Application running in network client mode.
 * SERVER             - Application running in server mode.
*/
public enum RunMode {
    /** 
     * User started Manufacturer application with a mode of "client" so this will be
     * a non-networked client - no network access. 
     */
    NON_NETWORK_CLIENT,

    /**
     * User didn't enter an application mode when starting Manufacturer application 
     * so this will be a networked client via RMI. 
     */
    NETWORK_CLIENT,
	
    /** 
     * User started Manufacturer application with a mode of "server" so we need to
     * setup a server connection. 
     */
    SERVER
}

Compiling Our Source File With the -cp and -d Options

Open your command line editor:

Change to directory  cd c:\_Case_Study\src\client

Compile RunMode.java using the java compiler with the -cp and -d options
  javac -cp ..\..\classes -d ..\..\classes RunMode.java

Compiling The RunModeOptionsUpdate Classgo to top of page Top

The RunModeOptionsUpdate class is a Value object used to transfer information about changes to our RunModeOptions panel to any interested observers.

Cut and paste the following code into your text editor and save it in the   c:\_Case_Study\src\client directory.


package client;

import java.util.logging.Logger;

/**
 * Value object used to transfer information about changes to our
 * RunModeOptions panel to any interested observers. 
 *
 */
public class RunModeOptionsUpdate {
    /**
     * The Logger instance. All log messages from this class are routed through
     * this member. The Logger namespace is s2cCaseStudy.
     */
	private static Logger log = Logger.getLogger("s2cCaseStudy"); // Log output

    /**
     * The enumerated list of possible updates that can be sent from the
     * RunModeOptions panel. Only one of the following options can possibly 
     * be passed with this value object.
     */
    public enum Update {
        /**
         * The user has specified the location of the Manufacturer file 
         * or the address of the server.
         */
        FILE_LOCATION_MODIFIED,
        /**
         * The user has changed the port number the server is expected to be
         * listening on.
         */
        PORT_MODIFIED;
    }

    /*
     * The values that will be transfered.
     */
    private Update updateType = null;
    private Object payload = null;

    /**
     * Empty constructor to conform with JavaBean requirements.
     */
    public RunModeOptionsUpdate() {
        log.entering("RunModeOptionsUpdate", "RunModeOptionsUpdate");
        log.exiting("RunModeOptionsUpdate", "RunModeOptionsUpdate");
    }

    /**
     * Constructor that allows us to specify the update type and any 
     * relevant information. 
     *
     * @param updateType The type of run mode update that has occurred.
     * @param payload Any relevant information that we would like to pass at
     * the same time.
     */
    public RunModeOptionsUpdate(Update updateType, Object payload) {
        log.entering("RunModeOptionsUpdate", "RunModeOptionsUpdate", 
                new Object[] {updateType, payload});
        this.updateType = updateType;
        this.payload = payload;
        log.exiting("RunModeOptionsUpdate", "RunModeOptionsUpdate");
    }
    
    /*
     *  Getter and setter methods for JavaBean requirements
     */
    /**
     * Gets the type of run mode update that has occurred.
     *
     * @return The type of update that has occurred.
     */
    public Update getUpdateType() {
        log.entering("RunModeOptionsUpdate", "getUpdateType");
        log.exiting("RunModeOptionsUpdate", "getUpdateType", this.updateType);
        return this.updateType;
    }

    /**
     * Sets the type of run mode update that has occurred.
     *
     * @param updateType The type of update that has occurred.
     */
    public void setUpdateType(Update updateType) {
        log.entering("RunModeOptionsUpdate", "setUpdateType");
        this.updateType = updateType;
        log.exiting("RunModeOptionsUpdate", "setUpdateType");
    }

    /**
     * Gets any information considered relevant to this update.
     *
     * @return Any relevant information that we would like to pass at
     * the same time.
     */
    public Object getPayload() {
        log.entering("RunModeOptionsUpdate", "getPayload");
        log.exiting("RunModeOptionsUpdate", "getPayload", this.payload);
        return this.payload;
    }

    /**
     * Sets any information considered relevant to this update.
     *
     * @param payload Any relevant information that we would like to pass at
     * the same time.
     */
    public void setPayload(Object payload) {
        log.entering("RunModeOptionsUpdate", "setPayload");
        this.payload = payload;
        log.exiting("RunModeOptionsUpdate", "setPayload");
    }
}

Compiling Our Source File With the -cp and -d Options

Open your command line editor:

Change to directory  cd c:\_Case_Study\src\client

Compile RunModeOptionsUpdate.java using the java compiler with the -cp and -d options
  javac -cp ..\..\classes -d ..\..\classes RunModeOptionsUpdate.java

The following screenshot shows that we get a clean compile on the above classes and also the ExitManufacturerFileCleanly, ManufacturerTableModel, PositiveIntVerify, RunMode and RunModeOptionsUpdate classes are now compiled into the classes\client directory.

compile Independent classes

Related Java6 Tutorials

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 - Getters & Setters
Swing - RMI - Serialization


What's Next?

In the next lesson we look at the code for the Manufacturer application tsart up and also the saved run mode / run mode options.

go to home page Homepage go to home page Top